diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-05-09 14:22:11 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2016-05-09 15:11:45 +0000 |
commit | 2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c (patch) | |
tree | e75f511546c5fd1a173e87c1f9fb11d7ac8d1af3 /chromium/ui | |
parent | a4f3d46271c57e8155ba912df46a05559d14726e (diff) | |
download | qtwebengine-chromium-2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c.tar.gz |
BASELINE: Update Chromium to 51.0.2704.41
Also adds in all smaller components by reversing logic for exclusion.
Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/ui')
1207 files changed, 36737 insertions, 20504 deletions
diff --git a/chromium/ui/OWNERS b/chromium/ui/OWNERS index b86b2dfb78c..79875b5f571 100644 --- a/chromium/ui/OWNERS +++ b/chromium/ui/OWNERS @@ -1,9 +1,10 @@ sadrul@chromium.org sky@chromium.org +thakis@chromium.org # Mac stuff avi@chromium.org -thakis@chromium.org +ccameron@chromium.org per-file *.isolate=maruel@chromium.org per-file *.isolate=tandrii@chromium.org diff --git a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h index 696dc188347..fea9a515e8d 100644 --- a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h +++ b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h @@ -51,7 +51,7 @@ class AcceleratedWidgetMacNSView { // in an NSView by setting the AcceleratedWidgetMacNSView for the helper. class ACCELERATED_WIDGET_MAC_EXPORT AcceleratedWidgetMac { public: - explicit AcceleratedWidgetMac(bool needs_gl_finish_workaround); + AcceleratedWidgetMac(); virtual ~AcceleratedWidgetMac(); gfx::AcceleratedWidget accelerated_widget() { return native_widget_; } @@ -113,10 +113,6 @@ class ACCELERATED_WIDGET_MAC_EXPORT AcceleratedWidgetMac { // The size in DIP of the last swap received from |compositor_|. gfx::Size last_swap_size_dip_; - // Whether surfaces created by the widget should use the glFinish() workaround - // after compositing. - bool needs_gl_finish_workaround_; - DISALLOW_COPY_AND_ASSIGN(AcceleratedWidgetMac); }; diff --git a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm index 135f64c2e4b..fc9269bd20f 100644 --- a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm +++ b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm @@ -45,9 +45,7 @@ AcceleratedWidgetMac* GetHelperFromAcceleratedWidget( //////////////////////////////////////////////////////////////////////////////// // AcceleratedWidgetMac -AcceleratedWidgetMac::AcceleratedWidgetMac(bool needs_gl_finish_workaround) - : view_(NULL), - needs_gl_finish_workaround_(needs_gl_finish_workaround) { +AcceleratedWidgetMac::AcceleratedWidgetMac() : view_(nullptr) { // Disable the fade-in animation as the layers are added. ScopedCAActionDisabler disabler; diff --git a/chromium/ui/accelerated_widget_mac/display_link_mac.cc b/chromium/ui/accelerated_widget_mac/display_link_mac.cc index d9cc46876f9..72470f64b1e 100644 --- a/chromium/ui/accelerated_widget_mac/display_link_mac.cc +++ b/chromium/ui/accelerated_widget_mac/display_link_mac.cc @@ -25,6 +25,20 @@ struct ScopedTypeRefTraits<CVDisplayLinkRef> { } // namespace base +namespace { + +// Empty callback set during tear down. +CVReturn VoidDisplayLinkCallback(CVDisplayLinkRef display_link, + const CVTimeStamp* now, + const CVTimeStamp* output_time, + CVOptionFlags flags_in, + CVOptionFlags* flags_out, + void* context) { + return kCVReturnSuccess; +} + +} // namespace + namespace ui { // static @@ -86,6 +100,14 @@ DisplayLinkMac::DisplayLinkMac( DisplayLinkMac::~DisplayLinkMac() { StopDisplayLink(); + // Usually |display_link_| holds the last reference to CVDisplayLinkRef, but + // that's not guaranteed, so it might not free all resources after the + // destructor completes. Ensure the callback is cleared out regardless to + // avoid possible crashes (see http://crbug.com/564780). + CVReturn ret = CVDisplayLinkSetOutputCallback( + display_link_, VoidDisplayLinkCallback, nullptr); + DCHECK_EQ(kCGErrorSuccess, ret); + DisplayMap::iterator found = display_map_.Get().find(display_id_); DCHECK(found != display_map_.Get().end()); DCHECK(found->second == this); diff --git a/chromium/ui/accessibility/BUILD.gn b/chromium/ui/accessibility/BUILD.gn index 59f7eee48da..53d0801513c 100644 --- a/chromium/ui/accessibility/BUILD.gn +++ b/chromium/ui/accessibility/BUILD.gn @@ -50,6 +50,8 @@ component("accessibility") { "ax_text_utils.h", "ax_tree.cc", "ax_tree.h", + "ax_tree_combiner.cc", + "ax_tree_combiner.h", "ax_tree_data.cc", "ax_tree_data.h", "ax_tree_serializer.cc", @@ -74,6 +76,7 @@ component("accessibility") { public_deps = [ ":ax_gen", "//base", + "//base:i18n", "//ui/base", "//ui/gfx", "//ui/gfx/geometry", @@ -126,19 +129,11 @@ source_set("test_support") { ] } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("accessibility_unittests_run") { - testonly = true - deps = [ - ":accessibility_unittests", - ] -} - test("accessibility_unittests") { sources = [ "ax_generated_tree_unittest.cc", "ax_text_utils_unittest.cc", + "ax_tree_combiner_unittest.cc", "ax_tree_serializer_unittest.cc", "ax_tree_unittest.cc", "platform/ax_platform_node_win_unittest.cc", @@ -149,6 +144,7 @@ test("accessibility_unittests") { ":test_support", "//base", "//base/test:run_all_unittests", + "//skia", "//testing/gtest", "//ui/base", "//ui/gfx", diff --git a/chromium/ui/accessibility/accessibility.gyp b/chromium/ui/accessibility/accessibility.gyp index f32ca767d3f..5489aa13fac 100644 --- a/chromium/ui/accessibility/accessibility.gyp +++ b/chromium/ui/accessibility/accessibility.gyp @@ -17,6 +17,8 @@ 'hard_dependency': 1, 'dependencies': [ '../../base/base.gyp:base', + '../../base/base.gyp:base_i18n', + '../../skia/skia.gyp:skia', '../gfx/gfx.gyp:gfx', '../gfx/gfx.gyp:gfx_geometry', 'ax_gen', @@ -36,6 +38,8 @@ 'ax_text_utils.h', 'ax_tree.cc', 'ax_tree.h', + 'ax_tree_combiner.cc', + 'ax_tree_combiner.h', 'ax_tree_data.cc', 'ax_tree_data.h', 'ax_tree_serializer.cc', @@ -116,6 +120,7 @@ 'dependencies': [ '../../base/base.gyp:base', '../../base/base.gyp:run_all_unittests', + '../../skia/skia.gyp:skia', '../../testing/gtest.gyp:gtest', '../gfx/gfx.gyp:gfx', '../gfx/gfx.gyp:gfx_geometry', @@ -126,6 +131,7 @@ 'sources': [ 'ax_generated_tree_unittest.cc', 'ax_text_utils_unittest.cc', + 'ax_tree_combiner_unittest.cc', 'ax_tree_serializer_unittest.cc', 'ax_tree_unittest.cc', 'platform/ax_platform_node_win_unittest.cc' @@ -200,6 +206,7 @@ 'variables': { 'java_in_dir': '<(DEPTH)/build/android/empty', 'has_java_resources': 0, + 'never_lint': 1, }, 'dependencies': [ 'ax_enumerations_java', diff --git a/chromium/ui/accessibility/ax_enums.idl b/chromium/ui/accessibility/ax_enums.idl index 4bb7d9f9b10..be08a8a36d9 100644 --- a/chromium/ui/accessibility/ax_enums.idl +++ b/chromium/ui/accessibility/ax_enums.idl @@ -30,6 +30,7 @@ blur, // Remove: http://crbug.com/392502 checked_state_changed, // Implicit children_changed, + clicked, document_selection_changed, focus, hide, // Remove: http://crbug.com/392502 @@ -64,6 +65,7 @@ }; enum AXRole { + abbr, alert_dialog, alert, annotation, @@ -74,7 +76,7 @@ // TODO(nektar): Remove busy_indicator because it's used nowhere. busy_indicator, button, - button_drop_down, + button_drop_down, // Not used on Web. canvas, caption, cell, @@ -130,6 +132,7 @@ mark, marquee, math, + menu, menu_bar, menu_button, menu_item, @@ -137,7 +140,6 @@ menu_item_radio, menu_list_option, menu_list_popup, - menu, meter, navigation, note, @@ -204,7 +206,6 @@ enabled, // content only expanded, focusable, - focused, haspopup, horizontal, hovered, @@ -235,14 +236,18 @@ [cpp_enum_prefix_override="ax_attr"] enum AXStringAttribute { access_key, action, + // Only used when invalid_state == invalid_state_other. + aria_invalid_value, auto_complete, container_live_relevant, container_live_status, description, display, + // Only present when different from parent. + font_family, html_tag, - // Only used when invalid_state == invalid_state_other. - aria_invalid_value, + // Only present when different from parent. + language, name, live_relevant, live_status, diff --git a/chromium/ui/accessibility/ax_node_data.cc b/chromium/ui/accessibility/ax_node_data.cc index fe1b3db56cb..e199f0190f8 100644 --- a/chromium/ui/accessibility/ax_node_data.cc +++ b/chromium/ui/accessibility/ax_node_data.cc @@ -13,6 +13,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "ui/gfx/transform.h" using base::DoubleToString; using base::IntToString; @@ -65,6 +66,39 @@ AXNodeData::AXNodeData() AXNodeData::~AXNodeData() { } +AXNodeData::AXNodeData(const AXNodeData& other) { + id = other.id; + role = other.role; + state = other.state; + string_attributes = other.string_attributes; + int_attributes = other.int_attributes; + float_attributes = other.float_attributes; + bool_attributes = other.bool_attributes; + intlist_attributes = other.intlist_attributes; + html_attributes = other.html_attributes; + child_ids = other.child_ids; + location = other.location; + if (other.transform) + transform.reset(new gfx::Transform(*other.transform)); +} + +AXNodeData& AXNodeData::operator=(AXNodeData other) { + id = other.id; + role = other.role; + state = other.state; + string_attributes = other.string_attributes; + int_attributes = other.int_attributes; + float_attributes = other.float_attributes; + bool_attributes = other.bool_attributes; + intlist_attributes = other.intlist_attributes; + html_attributes = other.html_attributes; + child_ids = other.child_ids; + location = other.location; + if (other.transform) + transform.reset(new gfx::Transform(*other.transform)); + return *this; +} + bool AXNodeData::HasBoolAttribute(AXBoolAttribute attribute) const { auto iter = FindInVectorOfPairs(attribute, bool_attributes); return iter != bool_attributes.end(); @@ -273,8 +307,6 @@ std::string AXNodeData::ToString() const { result += " EXPANDED"; if (state & (1 << AX_STATE_FOCUSABLE)) result += " FOCUSABLE"; - if (state & (1 << AX_STATE_FOCUSED)) - result += " FOCUSED"; if (state & (1 << AX_STATE_HASPOPUP)) result += " HASPOPUP"; if (state & (1 << AX_STATE_HOVERED)) @@ -311,6 +343,9 @@ std::string AXNodeData::ToString() const { IntToString(location.width()) + ", " + IntToString(location.height()) + ")"; + if (transform && !transform->IsIdentity()) + result += " transform=" + transform->ToString(); + for (size_t i = 0; i < int_attributes.size(); ++i) { std::string value = IntToString(int_attributes[i].second); switch (int_attributes[i].first) { @@ -486,6 +521,9 @@ std::string AXNodeData::ToString() const { case AX_ATTR_ACTION: result += " action=" + value; break; + case AX_ATTR_ARIA_INVALID_VALUE: + result += " aria_invalid_value=" + value; + break; case AX_ATTR_AUTO_COMPLETE: result += " autocomplete=" + value; break; @@ -495,11 +533,14 @@ std::string AXNodeData::ToString() const { case AX_ATTR_DISPLAY: result += " display=" + value; break; + case AX_ATTR_FONT_FAMILY: + result += " font-family=" + value; + break; case AX_ATTR_HTML_TAG: result += " html_tag=" + value; break; - case AX_ATTR_ARIA_INVALID_VALUE: - result += " aria_invalid_value=" + value; + case AX_ATTR_LANGUAGE: + result += " language=" + value; break; case AX_ATTR_LIVE_RELEVANT: result += " relevant=" + value; diff --git a/chromium/ui/accessibility/ax_node_data.h b/chromium/ui/accessibility/ax_node_data.h index 5bb8c147ff5..067adaa3e93 100644 --- a/chromium/ui/accessibility/ax_node_data.h +++ b/chromium/ui/accessibility/ax_node_data.h @@ -11,12 +11,17 @@ #include <string> #include <vector> +#include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "base/strings/string_split.h" #include "ui/accessibility/ax_enums.h" #include "ui/accessibility/ax_export.h" #include "ui/gfx/geometry/rect.h" +namespace gfx { +class Transform; +}; + namespace ui { // A compact representation of the accessibility information for a @@ -26,6 +31,9 @@ struct AX_EXPORT AXNodeData { AXNodeData(); virtual ~AXNodeData(); + AXNodeData(const AXNodeData& other); + AXNodeData& operator=(AXNodeData other); + // Accessing accessibility attributes: // // There are dozens of possible attributes for an accessibility node, @@ -94,12 +102,11 @@ struct AX_EXPORT AXNodeData { bool IsRoot() const; void SetRoot(); - // This is a simple serializable struct. All member variables should be - // public and copyable. + // As much as possible this should behave as a simple, serializable, + // copyable struct. int32_t id; AXRole role; uint32_t state; - gfx::Rect location; std::vector<std::pair<AXStringAttribute, std::string> > string_attributes; std::vector<std::pair<AXIntAttribute, int32_t>> int_attributes; std::vector<std::pair<AXFloatAttribute, float> > float_attributes; @@ -108,6 +115,16 @@ struct AX_EXPORT AXNodeData { intlist_attributes; base::StringPairs html_attributes; std::vector<int32_t> child_ids; + + // The object's location relative to its window or frame. + gfx::Rect location; + + // An additional transform to apply to position this object and its subtree. + // NOTE: this member is a scoped_ptr because it's rare and gfx::Transform + // takes up a fair amount of space. The assignment operator and copy + // constructor both make a duplicate of the owned pointer, so it acts more + // like a member than a pointer. + scoped_ptr<gfx::Transform> transform; }; } // namespace ui diff --git a/chromium/ui/accessibility/ax_text_utils.cc b/chromium/ui/accessibility/ax_text_utils.cc index 1e68559027e..698e53b9613 100644 --- a/chromium/ui/accessibility/ax_text_utils.cc +++ b/chromium/ui/accessibility/ax_text_utils.cc @@ -4,6 +4,7 @@ #include "ui/accessibility/ax_text_utils.h" +#include "base/i18n/break_iterator.h" #include "base/logging.h" #include "base/strings/string_util.h" @@ -25,7 +26,16 @@ size_t FindAccessibleTextBoundary(const base::string16& text, return start_offset + 1; else return start_offset; - } else if (boundary == LINE_BOUNDARY) { + } + + base::i18n::BreakIterator word_iter(text, + base::i18n::BreakIterator::BREAK_WORD); + if (boundary == WORD_BOUNDARY) { + if (!word_iter.Init()) + return start_offset; + } + + if (boundary == LINE_BOUNDARY) { if (direction == FORWARDS_DIRECTION) { for (size_t j = 0; j < line_breaks.size(); ++j) { size_t line_break = line_breaks[j] >= 0 ? line_breaks[j] : 0; @@ -62,8 +72,12 @@ size_t FindAccessibleTextBoundary(const base::string16& text, NOTREACHED(); // These are handled above. break; case WORD_BOUNDARY: - if (base::IsUnicodeWhitespace(text[pos])) - return result; + if (word_iter.IsStartOfWord(result)) { + // If we are searching forward and we are still at the start offset, + // we need to find the next word. + if (direction == BACKWARDS_DIRECTION || result != start_offset) + return result; + } break; case PARAGRAPH_BOUNDARY: if (text[pos] == '\n') @@ -89,4 +103,4 @@ size_t FindAccessibleTextBoundary(const base::string16& text, } } -} // Namespace ui +} // namespace ui diff --git a/chromium/ui/accessibility/ax_text_utils_unittest.cc b/chromium/ui/accessibility/ax_text_utils_unittest.cc index 1c2a4a1e4e1..2c7f491d619 100644 --- a/chromium/ui/accessibility/ax_text_utils_unittest.cc +++ b/chromium/ui/accessibility/ax_text_utils_unittest.cc @@ -10,6 +10,49 @@ namespace ui { +TEST(AXTextUtils, FindAccessibleTextBoundaryWord) { + const base::string16 text = + base::UTF8ToUTF16("Hello there.This/is\ntesting."); + const size_t text_length = text.length(); + std::vector<int> line_start_offsets; + line_start_offsets.push_back(19); + size_t result; + + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 0, FORWARDS_DIRECTION); + EXPECT_EQ(6UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 5, BACKWARDS_DIRECTION); + EXPECT_EQ(0UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 6, FORWARDS_DIRECTION); + EXPECT_EQ(12UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 11, BACKWARDS_DIRECTION); + EXPECT_EQ(6UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 12, BACKWARDS_DIRECTION); + EXPECT_EQ(12UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 15, FORWARDS_DIRECTION); + EXPECT_EQ(17UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 15, BACKWARDS_DIRECTION); + EXPECT_EQ(12UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 16, FORWARDS_DIRECTION); + EXPECT_EQ(17UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 17, FORWARDS_DIRECTION); + EXPECT_EQ(20UL, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + 20, FORWARDS_DIRECTION); + EXPECT_EQ(text_length, result); + result = FindAccessibleTextBoundary(text, line_start_offsets, WORD_BOUNDARY, + text_length, BACKWARDS_DIRECTION); + EXPECT_EQ(20UL, result); +} + TEST(AXTextUtils, FindAccessibleTextBoundaryLine) { const base::string16 text = base::UTF8ToUTF16("Line 1.\nLine 2\n\t"); const size_t text_length = text.length(); @@ -18,7 +61,6 @@ TEST(AXTextUtils, FindAccessibleTextBoundaryLine) { line_start_offsets.push_back(15); size_t result; - // Basic cases. result = FindAccessibleTextBoundary(text, line_start_offsets, LINE_BOUNDARY, 5, FORWARDS_DIRECTION); @@ -30,9 +72,7 @@ TEST(AXTextUtils, FindAccessibleTextBoundaryLine) { 10, FORWARDS_DIRECTION); EXPECT_EQ(15UL, result); - // Edge cases. - result = FindAccessibleTextBoundary(text, line_start_offsets, LINE_BOUNDARY, text_length, BACKWARDS_DIRECTION); EXPECT_EQ(15UL, result); @@ -68,4 +108,4 @@ TEST(AXTextUtils, FindAccessibleTextBoundaryLine) { EXPECT_EQ(text_length, result); } -} // Namespace ui. +} // namespace ui diff --git a/chromium/ui/accessibility/ax_tree.cc b/chromium/ui/accessibility/ax_tree.cc index bd879eaac44..6282fb75c10 100644 --- a/chromium/ui/accessibility/ax_tree.cc +++ b/chromium/ui/accessibility/ax_tree.cc @@ -180,6 +180,8 @@ bool AXTree::UpdateNode(const AXNodeData& src, AXNode* node = GetFromId(src.id); if (node) { update_state->pending_nodes.erase(node); + if (delegate_) + delegate_->OnNodeDataWillChange(this, node->data(), src); node->SetData(src); } else { if (src.role != AX_ROLE_ROOT_WEB_AREA && diff --git a/chromium/ui/accessibility/ax_tree.h b/chromium/ui/accessibility/ax_tree.h index 62b0709fc35..888170749a4 100644 --- a/chromium/ui/accessibility/ax_tree.h +++ b/chromium/ui/accessibility/ax_tree.h @@ -41,6 +41,11 @@ class AX_EXPORT AXTreeDelegate { AXTreeDelegate(); virtual ~AXTreeDelegate(); + // Called before a node's data gets updated. + virtual void OnNodeDataWillChange(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) = 0; + // Called when tree data changes. virtual void OnTreeDataChanged(AXTree* tree) = 0; diff --git a/chromium/ui/accessibility/ax_tree_combiner.cc b/chromium/ui/accessibility/ax_tree_combiner.cc new file mode 100644 index 00000000000..56f7cacd1cb --- /dev/null +++ b/chromium/ui/accessibility/ax_tree_combiner.cc @@ -0,0 +1,238 @@ +// 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/accessibility/ax_tree.h" +#include "ui/accessibility/ax_tree_combiner.h" +#include "ui/gfx/geometry/rect_f.h" + +namespace ui { +namespace { + +// Return true if |attr| is a node ID that would need to be mapped when +// renumbering the ids in a combined tree. +bool IsNodeIdIntAttribute(AXIntAttribute attr) { + switch (attr) { + case AX_ATTR_ACTIVEDESCENDANT_ID: + case AX_ATTR_TABLE_HEADER_ID: + case AX_ATTR_TABLE_COLUMN_HEADER_ID: + case AX_ATTR_TABLE_ROW_HEADER_ID: + return true; + + // Note: all of the attributes are included here explicitly, + // rather than using "default:", so that it's a compiler error to + // add a new attribute without explicitly considering whether it's + // a node id attribute or not. + case AX_INT_ATTRIBUTE_NONE: + case AX_ATTR_SCROLL_X: + case AX_ATTR_SCROLL_X_MIN: + case AX_ATTR_SCROLL_X_MAX: + case AX_ATTR_SCROLL_Y: + case AX_ATTR_SCROLL_Y_MIN: + case AX_ATTR_SCROLL_Y_MAX: + case AX_ATTR_TEXT_SEL_START: + case AX_ATTR_TEXT_SEL_END: + case AX_ATTR_TABLE_ROW_COUNT: + case AX_ATTR_TABLE_COLUMN_COUNT: + case AX_ATTR_TABLE_ROW_INDEX: + case AX_ATTR_TABLE_COLUMN_INDEX: + case AX_ATTR_TABLE_CELL_COLUMN_INDEX: + case AX_ATTR_TABLE_CELL_COLUMN_SPAN: + case AX_ATTR_TABLE_CELL_ROW_INDEX: + case AX_ATTR_TABLE_CELL_ROW_SPAN: + case AX_ATTR_SORT_DIRECTION: + case AX_ATTR_HIERARCHICAL_LEVEL: + case AX_ATTR_NAME_FROM: + case AX_ATTR_DESCRIPTION_FROM: + case AX_ATTR_CHILD_TREE_ID: + case AX_ATTR_SET_SIZE: + case AX_ATTR_POS_IN_SET: + case AX_ATTR_COLOR_VALUE: + case AX_ATTR_BACKGROUND_COLOR: + case AX_ATTR_COLOR: + case AX_ATTR_INVALID_STATE: + case AX_ATTR_TEXT_DIRECTION: + case AX_ATTR_TEXT_STYLE: + return false; + } + + NOTREACHED(); + return false; +} + +// Return true if |attr| contains a vector of node ids that would need +// to be mapped when renumbering the ids in a combined tree. +bool IsNodeIdIntListAttribute(AXIntListAttribute attr) { + switch (attr) { + case AX_ATTR_CELL_IDS: + case AX_ATTR_CONTROLS_IDS: + case AX_ATTR_DESCRIBEDBY_IDS: + case AX_ATTR_FLOWTO_IDS: + case AX_ATTR_INDIRECT_CHILD_IDS: + case AX_ATTR_LABELLEDBY_IDS: + case AX_ATTR_UNIQUE_CELL_IDS: + return true; + + // Note: all of the attributes are included here explicitly, + // rather than using "default:", so that it's a compiler error to + // add a new attribute without explicitly considering whether it's + // a node id attribute or not. + case AX_INT_LIST_ATTRIBUTE_NONE: + case AX_ATTR_LINE_BREAKS: + case AX_ATTR_CHARACTER_OFFSETS: + case AX_ATTR_WORD_STARTS: + case AX_ATTR_WORD_ENDS: + return false; + } + + NOTREACHED(); + return false; +} + +} // namespace + +AXTreeCombiner::AXTreeCombiner() { +} + +AXTreeCombiner::~AXTreeCombiner() { +} + +void AXTreeCombiner::AddTree(const AXTreeUpdate& tree, bool is_root) { + trees_.push_back(tree); + if (is_root) { + DCHECK_EQ(root_tree_id_, -1); + root_tree_id_ = tree.tree_data.tree_id; + } +} + +bool AXTreeCombiner::Combine() { + // First create a map from tree ID to tree update. + for (const auto& tree : trees_) { + int32_t tree_id = tree.tree_data.tree_id; + if (tree_id_map_.find(tree_id) != tree_id_map_.end()) + return false; + tree_id_map_[tree.tree_data.tree_id] = &tree; + } + + // Make sure the root tree ID is in the map, otherwise fail. + if (tree_id_map_.find(root_tree_id_) == tree_id_map_.end()) + return false; + + // Process the nodes recursively, starting with the root tree. + const AXTreeUpdate* root = tree_id_map_.find(root_tree_id_)->second; + ProcessTree(root); + + // Finally, handle the tree ID, taking into account which subtree might + // have focus and mapping IDs from the tree data appropriately. + combined_.has_tree_data = true; + combined_.tree_data = root->tree_data; + int32_t focused_tree_id = root->tree_data.focused_tree_id; + const AXTreeUpdate* focused_tree = root; + if (tree_id_map_.find(focused_tree_id) != tree_id_map_.end()) + focused_tree = tree_id_map_[focused_tree_id]; + combined_.tree_data.focus_id = + MapId(focused_tree_id, focused_tree->tree_data.focus_id); + combined_.tree_data.sel_anchor_object_id = + MapId(focused_tree_id, focused_tree->tree_data.sel_anchor_object_id); + combined_.tree_data.sel_focus_object_id = + MapId(focused_tree_id, focused_tree->tree_data.sel_focus_object_id); + combined_.tree_data.sel_anchor_offset = + focused_tree->tree_data.sel_anchor_offset; + combined_.tree_data.sel_focus_offset = + focused_tree->tree_data.sel_focus_offset; + + // Debug-mode check that the resulting combined tree is valid. + AXTree tree; + DCHECK(tree.Unserialize(combined_)) + << combined_.ToString() << "\n" << tree.error(); + + return true; +} + +int32_t AXTreeCombiner::MapId(int32_t tree_id, int32_t node_id) { + auto tree_id_node_id = std::make_pair(tree_id, node_id); + if (tree_id_node_id_map_[tree_id_node_id] == 0) + tree_id_node_id_map_[tree_id_node_id] = next_id_++; + return tree_id_node_id_map_[tree_id_node_id]; +} + +void AXTreeCombiner::ProcessTree(const AXTreeUpdate* tree) { + // The root of each tree may contain a transform that needs to apply + // to all of its descendants. + gfx::Transform old_transform = transform_; + if (!tree->nodes.empty() && tree->nodes[0].transform) + transform_.ConcatTransform(*tree->nodes[0].transform); + + int32_t tree_id = tree->tree_data.tree_id; + for (size_t i = 0; i < tree->nodes.size(); ++i) { + AXNodeData node = tree->nodes[i]; + int32_t child_tree_id = node.GetIntAttribute(AX_ATTR_CHILD_TREE_ID); + + // It's not valid to have an accessibility tree with more than one + // ROOT_WEB_AREA role, so change subdocuments into just groups. + if (node.role == AX_ROLE_ROOT_WEB_AREA && tree_id != root_tree_id_) + node.role = AX_ROLE_GROUP; + + // Map the node's ID. + node.id = MapId(tree_id, node.id); + + // Map the node's child IDs. + for (size_t j = 0; j < node.child_ids.size(); ++j) + node.child_ids[j] = MapId(tree_id, node.child_ids[j]); + + // Map other int attributes that refer to node IDs, and remove the + // AX_ATTR_CHILD_TREE_ID attribute. + for (size_t j = 0; j < node.int_attributes.size(); ++j) { + auto& attr = node.int_attributes[j]; + if (IsNodeIdIntAttribute(attr.first)) + attr.second = MapId(tree_id, attr.second); + if (attr.first == AX_ATTR_CHILD_TREE_ID) { + attr.first = AX_INT_ATTRIBUTE_NONE; + attr.second = 0; + } + } + + // Map other int list attributes that refer to node IDs. + for (size_t j = 0; j < node.intlist_attributes.size(); ++j) { + auto& attr = node.intlist_attributes[j]; + if (IsNodeIdIntListAttribute(attr.first)) { + for (size_t k = 0; k < attr.second.size(); k++) + attr.second[k] = MapId(tree_id, attr.second[k]); + } + } + + // Apply the transformation to the object's bounds to put it in + // the coordinate space of the root frame. + gfx::RectF boundsf(node.location); + transform_.TransformRect(&boundsf); + node.location = gfx::Rect(boundsf.x(), boundsf.y(), + boundsf.width(), boundsf.height()); + + // See if this node has a child tree. As a sanity check make sure the + // child tree lists this tree as its parent tree id. + const AXTreeUpdate* child_tree = nullptr; + if (tree_id_map_.find(child_tree_id) != tree_id_map_.end()) { + child_tree = tree_id_map_.find(child_tree_id)->second; + if (child_tree->tree_data.parent_tree_id != tree_id) + child_tree = nullptr; + if (child_tree && child_tree->nodes.empty()) + child_tree = nullptr; + if (child_tree) { + node.child_ids.push_back(MapId(child_tree_id, + child_tree->nodes[0].id)); + } + } + + // Put the rewritten AXNodeData into the output data structure. + combined_.nodes.push_back(node); + + // Recurse into the child tree now, if any. + if (child_tree) + ProcessTree(child_tree); + } + + // Reset the transform. + transform_ = old_transform; +} + +} // namespace ui diff --git a/chromium/ui/accessibility/ax_tree_combiner.h b/chromium/ui/accessibility/ax_tree_combiner.h new file mode 100644 index 00000000000..37b9fa56879 --- /dev/null +++ b/chromium/ui/accessibility/ax_tree_combiner.h @@ -0,0 +1,54 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_ACCESSIBILITY_AX_TREE_COMBINER_H_ +#define UI_ACCESSIBILITY_AX_TREE_COMBINER_H_ + +#include <vector> + +#include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_tree_update.h" +#include "ui/gfx/transform.h" + +namespace ui { + +// This helper class takes multiple accessibility trees that reference each +// other via tree IDs, and combines them into a single accessibility tree +// that spans all of them. +// +// Since node IDs are relative to each ID, it has to renumber all of the IDs +// and update all of the attributes that reference IDs of other nodes to +// ensure they point to the right node. It also applies transformations to +// local bounding rectangles to make them global. +// +// It also makes sure the final combined tree points to the correct focused +// node across all of the trees based on the focused tree ID of the root tree. +class AX_EXPORT AXTreeCombiner { + public: + AXTreeCombiner(); + ~AXTreeCombiner(); + + void AddTree(const AXTreeUpdate& tree, bool is_root); + bool Combine(); + + const AXTreeUpdate& combined() { return combined_; } + + private: + int32_t MapId(int32_t tree_id, int32_t node_id); + + void ProcessTree(const AXTreeUpdate* tree); + + std::vector<ui::AXTreeUpdate> trees_; + int32_t root_tree_id_ = -1; + int32_t next_id_ = 1; + std::map<int32_t, const AXTreeUpdate*> tree_id_map_; + std::map<std::pair<int32_t, int32_t>, int32_t> tree_id_node_id_map_; + AXTreeUpdate combined_; + gfx::Transform transform_; +}; + + +} // namespace ui + +#endif // UI_ACCESSIBILITY_AX_TREE_COMBINER_H_ diff --git a/chromium/ui/accessibility/ax_tree_combiner_unittest.cc b/chromium/ui/accessibility/ax_tree_combiner_unittest.cc new file mode 100644 index 00000000000..0d4e6980b16 --- /dev/null +++ b/chromium/ui/accessibility/ax_tree_combiner_unittest.cc @@ -0,0 +1,244 @@ +// 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 "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/ax_tree_combiner.h" + +namespace ui { + +TEST(CombineAXTreesTest, RenumberOneTree) { + AXTreeUpdate tree; + tree.has_tree_data = true; + tree.tree_data.tree_id = 1; + tree.nodes.resize(3); + tree.nodes[0].id = 2; + tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + tree.nodes[0].child_ids.push_back(4); + tree.nodes[0].child_ids.push_back(6); + tree.nodes[1].id = 4; + tree.nodes[2].id = 6; + + AXTreeCombiner combiner; + combiner.AddTree(tree, true); + combiner.Combine(); + + const AXTreeUpdate& combined = combiner.combined(); + + ASSERT_EQ(3U, combined.nodes.size()); + EXPECT_EQ(1, combined.nodes[0].id); + ASSERT_EQ(2U, combined.nodes[0].child_ids.size()); + EXPECT_EQ(2, combined.nodes[0].child_ids[0]); + EXPECT_EQ(3, combined.nodes[0].child_ids[1]); + EXPECT_EQ(2, combined.nodes[1].id); + EXPECT_EQ(3, combined.nodes[2].id); +} + +TEST(CombineAXTreesTest, EmbedChildTree) { + AXTreeUpdate parent_tree; + parent_tree.has_tree_data = true; + parent_tree.tree_data.tree_id = 1; + parent_tree.nodes.resize(3); + parent_tree.nodes[0].id = 1; + parent_tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + parent_tree.nodes[0].child_ids.push_back(2); + parent_tree.nodes[0].child_ids.push_back(3); + parent_tree.nodes[1].id = 2; + parent_tree.nodes[1].role = AX_ROLE_BUTTON; + parent_tree.nodes[2].id = 3; + parent_tree.nodes[2].role = AX_ROLE_IFRAME; + parent_tree.nodes[2].AddIntAttribute(AX_ATTR_CHILD_TREE_ID, 2); + + AXTreeUpdate child_tree; + child_tree.has_tree_data = true; + child_tree.tree_data.parent_tree_id = 1; + child_tree.tree_data.tree_id = 2; + child_tree.nodes.resize(3); + child_tree.nodes[0].id = 1; + child_tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + child_tree.nodes[0].child_ids.push_back(2); + child_tree.nodes[0].child_ids.push_back(3); + child_tree.nodes[1].id = 2; + child_tree.nodes[1].role = AX_ROLE_CHECK_BOX; + child_tree.nodes[2].id = 3; + child_tree.nodes[2].role = AX_ROLE_RADIO_BUTTON; + + AXTreeCombiner combiner; + combiner.AddTree(parent_tree, true); + combiner.AddTree(child_tree, false); + combiner.Combine(); + + const AXTreeUpdate& combined = combiner.combined(); + + ASSERT_EQ(6U, combined.nodes.size()); + EXPECT_EQ(1, combined.nodes[0].id); + ASSERT_EQ(2U, combined.nodes[0].child_ids.size()); + EXPECT_EQ(2, combined.nodes[0].child_ids[0]); + EXPECT_EQ(3, combined.nodes[0].child_ids[1]); + EXPECT_EQ(2, combined.nodes[1].id); + EXPECT_EQ(AX_ROLE_BUTTON, combined.nodes[1].role); + EXPECT_EQ(3, combined.nodes[2].id); + EXPECT_EQ(AX_ROLE_IFRAME, combined.nodes[2].role); + EXPECT_EQ(1U, combined.nodes[2].child_ids.size()); + EXPECT_EQ(4, combined.nodes[2].child_ids[0]); + EXPECT_EQ(4, combined.nodes[3].id); + EXPECT_EQ(AX_ROLE_GROUP, combined.nodes[3].role); + EXPECT_EQ(5, combined.nodes[4].id); + EXPECT_EQ(AX_ROLE_CHECK_BOX, combined.nodes[4].role); + EXPECT_EQ(6, combined.nodes[5].id); + EXPECT_EQ(AX_ROLE_RADIO_BUTTON, combined.nodes[5].role); +} + +TEST(CombineAXTreesTest, MapAllIdAttributes) { + // This is a nonsensical accessibility tree, the goal is to make sure + // that all attributes that reference IDs of other nodes are remapped. + + AXTreeUpdate tree; + tree.has_tree_data = true; + tree.tree_data.tree_id = 1; + tree.nodes.resize(2); + tree.nodes[0].id = 11; + tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + tree.nodes[0].child_ids.push_back(22); + tree.nodes[0].AddIntAttribute(AX_ATTR_TABLE_HEADER_ID, 22); + tree.nodes[0].AddIntAttribute(AX_ATTR_TABLE_ROW_HEADER_ID, 22); + tree.nodes[0].AddIntAttribute(AX_ATTR_TABLE_COLUMN_HEADER_ID, 22); + tree.nodes[0].AddIntAttribute(AX_ATTR_ACTIVEDESCENDANT_ID, 22); + std::vector<int32_t> ids { 22 }; + tree.nodes[0].AddIntListAttribute(AX_ATTR_INDIRECT_CHILD_IDS, ids); + tree.nodes[0].AddIntListAttribute(AX_ATTR_CONTROLS_IDS, ids); + tree.nodes[0].AddIntListAttribute(AX_ATTR_DESCRIBEDBY_IDS, ids); + tree.nodes[0].AddIntListAttribute(AX_ATTR_FLOWTO_IDS, ids); + tree.nodes[0].AddIntListAttribute(AX_ATTR_LABELLEDBY_IDS, ids); + tree.nodes[0].AddIntListAttribute(AX_ATTR_CELL_IDS, ids); + tree.nodes[0].AddIntListAttribute(AX_ATTR_UNIQUE_CELL_IDS, ids); + tree.nodes[1].id = 22; + + AXTreeCombiner combiner; + combiner.AddTree(tree, true); + combiner.Combine(); + + const AXTreeUpdate& combined = combiner.combined(); + + ASSERT_EQ(2U, combined.nodes.size()); + EXPECT_EQ(1, combined.nodes[0].id); + ASSERT_EQ(1U, combined.nodes[0].child_ids.size()); + EXPECT_EQ(2, combined.nodes[0].child_ids[0]); + EXPECT_EQ(2, combined.nodes[1].id); + + EXPECT_EQ(2, combined.nodes[0].GetIntAttribute(AX_ATTR_TABLE_HEADER_ID)); + EXPECT_EQ(2, combined.nodes[0].GetIntAttribute(AX_ATTR_TABLE_ROW_HEADER_ID)); + EXPECT_EQ(2, combined.nodes[0].GetIntAttribute( + AX_ATTR_TABLE_COLUMN_HEADER_ID)); + EXPECT_EQ(2, combined.nodes[0].GetIntAttribute(AX_ATTR_ACTIVEDESCENDANT_ID)); + EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute( + AX_ATTR_INDIRECT_CHILD_IDS)[0]); + EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute( + AX_ATTR_CONTROLS_IDS)[0]); + EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute( + AX_ATTR_DESCRIBEDBY_IDS)[0]); + EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute( + AX_ATTR_FLOWTO_IDS)[0]); + EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute( + AX_ATTR_LABELLEDBY_IDS)[0]); + EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute( + AX_ATTR_CELL_IDS)[0]); + EXPECT_EQ(2, combined.nodes[0].GetIntListAttribute( + AX_ATTR_UNIQUE_CELL_IDS)[0]); +} + +TEST(CombineAXTreesTest, Coordinates) { + AXTreeUpdate parent_tree; + parent_tree.has_tree_data = true; + parent_tree.tree_data.tree_id = 1; + parent_tree.nodes.resize(3); + parent_tree.nodes[0].id = 1; + parent_tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + parent_tree.nodes[0].child_ids.push_back(2); + parent_tree.nodes[0].child_ids.push_back(3); + parent_tree.nodes[1].id = 2; + parent_tree.nodes[1].role = AX_ROLE_BUTTON; + parent_tree.nodes[1].location = gfx::Rect(50, 10, 200, 100); + parent_tree.nodes[2].id = 3; + parent_tree.nodes[2].role = AX_ROLE_IFRAME; + parent_tree.nodes[2].AddIntAttribute(AX_ATTR_CHILD_TREE_ID, 2); + + AXTreeUpdate child_tree; + child_tree.has_tree_data = true; + child_tree.tree_data.parent_tree_id = 1; + child_tree.tree_data.tree_id = 2; + child_tree.nodes.resize(2); + child_tree.nodes[0].id = 1; + child_tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + child_tree.nodes[0].child_ids.push_back(2); + + child_tree.nodes[0].transform.reset(new gfx::Transform()); + child_tree.nodes[0].transform->Translate(0, 300); + child_tree.nodes[0].transform->Scale(2.0, 2.0); + + child_tree.nodes[1].id = 2; + child_tree.nodes[1].role = AX_ROLE_BUTTON; + child_tree.nodes[1].location = gfx::Rect(50, 10, 200, 100); + + AXTreeCombiner combiner; + combiner.AddTree(parent_tree, true); + combiner.AddTree(child_tree, false); + combiner.Combine(); + + const AXTreeUpdate& combined = combiner.combined(); + + ASSERT_EQ(5U, combined.nodes.size()); + EXPECT_EQ(50, combined.nodes[1].location.x()); + EXPECT_EQ(10, combined.nodes[1].location.y()); + EXPECT_EQ(200, combined.nodes[1].location.width()); + EXPECT_EQ(100, combined.nodes[1].location.height()); + EXPECT_EQ(100, combined.nodes[4].location.x()); + EXPECT_EQ(320, combined.nodes[4].location.y()); + EXPECT_EQ(400, combined.nodes[4].location.width()); + EXPECT_EQ(200, combined.nodes[4].location.height()); +} + +TEST(CombineAXTreesTest, FocusedTree) { + AXTreeUpdate parent_tree; + parent_tree.has_tree_data = true; + parent_tree.tree_data.tree_id = 1; + parent_tree.tree_data.focused_tree_id = 2; + parent_tree.tree_data.focus_id = 2; + parent_tree.nodes.resize(3); + parent_tree.nodes[0].id = 1; + parent_tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + parent_tree.nodes[0].child_ids.push_back(2); + parent_tree.nodes[0].child_ids.push_back(3); + parent_tree.nodes[1].id = 2; + parent_tree.nodes[1].role = AX_ROLE_BUTTON; + parent_tree.nodes[2].id = 3; + parent_tree.nodes[2].role = AX_ROLE_IFRAME; + parent_tree.nodes[2].AddIntAttribute(AX_ATTR_CHILD_TREE_ID, 2); + + AXTreeUpdate child_tree; + child_tree.has_tree_data = true; + child_tree.tree_data.parent_tree_id = 1; + child_tree.tree_data.tree_id = 2; + child_tree.tree_data.focus_id = 3; + child_tree.nodes.resize(3); + child_tree.nodes[0].id = 1; + child_tree.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; + child_tree.nodes[0].child_ids.push_back(2); + child_tree.nodes[0].child_ids.push_back(3); + child_tree.nodes[1].id = 2; + child_tree.nodes[1].role = AX_ROLE_CHECK_BOX; + child_tree.nodes[2].id = 3; + child_tree.nodes[2].role = AX_ROLE_RADIO_BUTTON; + + AXTreeCombiner combiner; + combiner.AddTree(parent_tree, true); + combiner.AddTree(child_tree, false); + combiner.Combine(); + + const AXTreeUpdate& combined = combiner.combined(); + + ASSERT_EQ(6U, combined.nodes.size()); + EXPECT_EQ(6, combined.tree_data.focus_id); +} + +} // namespace ui diff --git a/chromium/ui/accessibility/ax_tree_data.cc b/chromium/ui/accessibility/ax_tree_data.cc index 9475a597ff5..4e434dba306 100644 --- a/chromium/ui/accessibility/ax_tree_data.cc +++ b/chromium/ui/accessibility/ax_tree_data.cc @@ -19,14 +19,18 @@ namespace ui { AXTreeData::AXTreeData() : tree_id(-1), parent_tree_id(-1), + focused_tree_id(-1), loaded(false), loading_progress(0.0), + focus_id(-1), sel_anchor_object_id(-1), sel_anchor_offset(-1), sel_focus_object_id(-1), sel_focus_offset(-1) { } +AXTreeData::AXTreeData(const AXTreeData& other) = default; + AXTreeData::~AXTreeData() { } @@ -39,20 +43,24 @@ std::string AXTreeData::ToString() const { result += " tree_id=" + IntToString(tree_id); if (parent_tree_id != -1) result += " parent_tree_id=" + IntToString(parent_tree_id); + if (focused_tree_id != -1) + result += " focused_tree_id=" + IntToString(focused_tree_id); - if (!url.empty()) - result += " url=" + url; - if (!title.empty()) - result += " title=" + title; - if (!mimetype.empty()) - result += " mimetype=" + mimetype; if (!doctype.empty()) result += " doctype=" + doctype; - if (loaded) result += " loaded=true"; if (loading_progress != 0.0) result += " loading_progress=" + DoubleToString(loading_progress); + if (!mimetype.empty()) + result += " mimetype=" + mimetype; + if (!url.empty()) + result += " url=" + url; + if (!title.empty()) + result += " title=" + title; + + if (focus_id != -1) + result += " focus_id=" + IntToString(focus_id); if (sel_anchor_object_id != -1) { result += " sel_anchor_object_id=" + IntToString(sel_anchor_object_id); @@ -69,12 +77,11 @@ std::string AXTreeData::ToString() const { bool operator==(const AXTreeData& lhs, const AXTreeData& rhs) { return (lhs.tree_id == rhs.tree_id && lhs.parent_tree_id == rhs.parent_tree_id && - lhs.url == rhs.url && - lhs.title == rhs.title && - lhs.mimetype == rhs.mimetype && - lhs.doctype == rhs.doctype && - lhs.loaded == rhs.loaded && + lhs.focused_tree_id == rhs.focused_tree_id && + lhs.doctype == rhs.doctype && lhs.loaded == rhs.loaded && lhs.loading_progress == rhs.loading_progress && + lhs.mimetype == rhs.mimetype && lhs.title == rhs.title && + lhs.url == rhs.url && lhs.focus_id == rhs.focus_id && lhs.sel_anchor_object_id == rhs.sel_anchor_object_id && lhs.sel_anchor_offset == rhs.sel_anchor_offset && lhs.sel_focus_object_id == rhs.sel_focus_object_id && diff --git a/chromium/ui/accessibility/ax_tree_data.h b/chromium/ui/accessibility/ax_tree_data.h index 2922a18369a..83a15890666 100644 --- a/chromium/ui/accessibility/ax_tree_data.h +++ b/chromium/ui/accessibility/ax_tree_data.h @@ -23,6 +23,7 @@ namespace ui { // tree and not associated with any particular node in the tree. struct AX_EXPORT AXTreeData { AXTreeData(); + AXTreeData(const AXTreeData& other); virtual ~AXTreeData(); // Return a string representation of this data, for debugging. @@ -37,13 +38,21 @@ struct AX_EXPORT AXTreeData { // The ID of the accessibility tree that this tree is contained in, if any. int32_t parent_tree_id; + // The ID of the accessibility tree that has focus. This is typically set + // on the root frame in a frame tree. + int32_t focused_tree_id; + // Attributes specific to trees that are web frames. - std::string url; - std::string title; - std::string mimetype; std::string doctype; bool loaded; float loading_progress; + std::string mimetype; + std::string title; + std::string url; + + // The node with keyboard focus within this tree, if any, or -1 if no node + // in this tree has focus. + int32_t focus_id; // The current text selection within this tree, if any, expressed as the // node ID and character offset of the anchor (selection start) and focus diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc index 16706e757d0..27f8dae879b 100644 --- a/chromium/ui/accessibility/ax_tree_unittest.cc +++ b/chromium/ui/accessibility/ax_tree_unittest.cc @@ -23,6 +23,9 @@ class FakeAXTreeDelegate : public AXTreeDelegate { : tree_data_changed_(false), root_changed_(false) {} + void OnNodeDataWillChange(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) override {} void OnTreeDataChanged(AXTree* tree) override { tree_data_changed_ = true; } @@ -99,7 +102,7 @@ TEST(AXTreeTest, SerializeSimpleAXTree) { AXNodeData root; root.id = 1; root.role = AX_ROLE_ROOT_WEB_AREA; - root.state = (1 << AX_STATE_FOCUSABLE) | (1 << AX_STATE_FOCUSED); + root.state = 1 << AX_STATE_FOCUSABLE; root.location = gfx::Rect(0, 0, 800, 600); root.child_ids.push_back(2); root.child_ids.push_back(3); @@ -151,7 +154,7 @@ TEST(AXTreeTest, SerializeSimpleAXTree) { EXPECT_EQ( "AXTree title=Title\n" - "id=1 rootWebArea FOCUSABLE FOCUSED (0, 0)-(800, 600) child_ids=2,3\n" + "id=1 rootWebArea FOCUSABLE (0, 0)-(800, 600) child_ids=2,3\n" " id=2 button (20, 20)-(200, 30)\n" " id=3 checkBox (20, 50)-(200, 30)\n", dst_tree.ToString()); diff --git a/chromium/ui/accessibility/ax_tree_update.h b/chromium/ui/accessibility/ax_tree_update.h index 662bdfa88db..f9fb1fa2582 100644 --- a/chromium/ui/accessibility/ax_tree_update.h +++ b/chromium/ui/accessibility/ax_tree_update.h @@ -11,6 +11,7 @@ #include <string> #include <vector> +#include "base/containers/hash_tables.h" #include "base/strings/string_number_conversions.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_tree_data.h" diff --git a/chromium/ui/accessibility/extensions/alt/hide-images.css b/chromium/ui/accessibility/extensions/alt/hide-images.css index a9d30e333a9..840d7f23397 100644 --- a/chromium/ui/accessibility/extensions/alt/hide-images.css +++ b/chromium/ui/accessibility/extensions/alt/hide-images.css @@ -29,19 +29,19 @@ body[show-alt] img:not([aria-hidden=true]):not([role=presentation]):not([alt=''] @-webkit-keyframes slideDown { from { - -webkit-transform: translateY(-150%); + transform: translateY(-150%); } to { - -webkit-transform: translateY(0px); + transform: translateY(0px); } } @-webkit-keyframes slideUp { from { - -webkit-transform: translateY(0%); + transform: translateY(0%); } to { - -webkit-transform: translateY(-150%); + transform: translateY(-150%); } } @@ -52,7 +52,7 @@ body[show-alt] .show-alt-infobar { -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: ease; -webkit-animation-direction: forwards; - -webkit-transform: translateY(0%); + transform: translateY(0%); } body:not([show-alt]) .show-alt-infobar { @@ -62,7 +62,7 @@ body:not([show-alt]) .show-alt-infobar { -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: ease; -webkit-animation-direction: forwards; - -webkit-transform: translateY(-150%); + transform: translateY(-150%); } .show-alt-infobar { diff --git a/chromium/ui/accessibility/extensions/colorenhancer/res/setup.css b/chromium/ui/accessibility/extensions/colorenhancer/res/setup.css index 747b42b10b7..fbb1470005d 100644 --- a/chromium/ui/accessibility/extensions/colorenhancer/res/setup.css +++ b/chromium/ui/accessibility/extensions/colorenhancer/res/setup.css @@ -68,7 +68,7 @@ table input[type="button"] { } .swatch span { - -webkit-transform: translate(-5px, -22px); + transform: translate(-5px, -22px); font: 60pt sans; display: flex; height: 40px; diff --git a/chromium/ui/accessibility/platform/atk_util_auralinux.cc b/chromium/ui/accessibility/platform/atk_util_auralinux.cc index 02563199409..46ad3be53be 100644 --- a/chromium/ui/accessibility/platform/atk_util_auralinux.cc +++ b/chromium/ui/accessibility/platform/atk_util_auralinux.cc @@ -8,7 +8,9 @@ #endif #include <glib-2.0/gmodule.h> +#include "base/bind.h" #include "base/files/file_path.h" +#include "base/location.h" #include "base/logging.h" #include "base/memory/singleton.h" #include "ui/accessibility/platform/atk_util_auralinux.h" @@ -16,41 +18,42 @@ namespace { -#if defined(USE_GCONF) +typedef void (*gnome_accessibility_module_init)(); -const char kGnomeAccessibilityEnabledKey[] = - "/desktop/gnome/interface/accessibility"; +const char kAtkBridgePath[] = "gtk-2.0/modules/libatk-bridge.so"; +const char kAtkBridgeSymbolName[] = "gnome_accessibility_module_init"; -bool ShouldEnableAccessibility() { - GConfClient* client = gconf_client_get_default(); - if (!client) { - LOG(ERROR) << "gconf_client_get_default failed"; +gnome_accessibility_module_init g_accessibility_module_init = nullptr; + +bool AccessibilityModuleInitOnFileThread() { + // Try to load libatk-bridge.so. + base::FilePath atk_bridge_path(ATK_LIB_DIR); + atk_bridge_path = atk_bridge_path.Append(kAtkBridgePath); + GModule* bridge = g_module_open(atk_bridge_path.value().c_str(), + static_cast<GModuleFlags>(0)); + if (!bridge) { + VLOG(1) << "Unable to open module " << atk_bridge_path.value(); return false; } - GError* error = nullptr; - gboolean value = gconf_client_get_bool(client, - kGnomeAccessibilityEnabledKey, - &error); - if (error) { - VLOG(1) << "gconf_client_get_bool failed"; - g_error_free(error); - g_object_unref(client); + if (!g_module_symbol(bridge, kAtkBridgeSymbolName, + (gpointer *)&g_accessibility_module_init)) { + 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; } - g_object_unref(client); - return value; + return true; } -#else // !defined(USE_GCONF) +#if defined(USE_GCONF) -bool ShouldEnableAccessibility() { - // TODO(k.czech): implement this for non-GNOME desktops. - return false; -} +const char kAccessibilityEnabled[] = "ACCESSIBILITY_ENABLED"; +const char kGnomeAccessibilityEnabledKey[] = + "/desktop/gnome/interface/accessibility"; -#endif // defined(USE_GCONF) +#endif } // namespace @@ -139,44 +142,88 @@ AtkUtilAuraLinux* AtkUtilAuraLinux::GetInstance() { return base::Singleton<AtkUtilAuraLinux>::get(); } +#if defined(USE_GCONF) + +AtkUtilAuraLinux::AtkUtilAuraLinux() + : is_enabled_(false) { +} + +#else + AtkUtilAuraLinux::AtkUtilAuraLinux() { } +#endif // defined(USE_GCONF) + void AtkUtilAuraLinux::Initialize( - scoped_refptr<base::TaskRunner> /* init_task_runner */) { - // TODO(k.czech): use |init_task_runner| to post a task to do the - // initialization rather than doing it on this thread. - // http://crbug.com/468112 + scoped_refptr<base::TaskRunner> init_task_runner) { // Register our util class. g_type_class_unref(g_type_class_ref(ATK_UTIL_AURALINUX_TYPE)); - if (!ShouldEnableAccessibility()) { - VLOG(1) << "Will not enable ATK accessibility support."; - return; + init_task_runner->PostTaskAndReply( + FROM_HERE, + base::Bind( + &AtkUtilAuraLinux::CheckIfAccessibilityIsEnabledOnFileThread, + base::Unretained(this)), + base::Bind( + &AtkUtilAuraLinux::FinishAccessibilityInitOnUIThread, + base::Unretained(this))); +} + +AtkUtilAuraLinux::~AtkUtilAuraLinux() { +} + +#if defined(USE_GCONF) + +void AtkUtilAuraLinux::CheckIfAccessibilityIsEnabledOnFileThread() { + char* enable_accessibility = getenv(kAccessibilityEnabled); + if ((enable_accessibility && atoi(enable_accessibility) == 1) || + CheckPlatformAccessibilitySupportOnFileThread()) + is_enabled_ = AccessibilityModuleInitOnFileThread(); +} + +bool AtkUtilAuraLinux::CheckPlatformAccessibilitySupportOnFileThread() { + GConfClient* client = gconf_client_get_default(); + if (!client) { + LOG(ERROR) << "gconf_client_get_default failed"; + return false; } - VLOG(1) << "Enabling ATK accessibility support."; + GError* error = nullptr; + bool is_enabled = gconf_client_get_bool(client, + kGnomeAccessibilityEnabledKey, + &error); - // Try to load libatk-bridge.so. - base::FilePath atk_bridge_path(ATK_LIB_DIR); - atk_bridge_path = atk_bridge_path.Append("gtk-2.0/modules/libatk-bridge.so"); - GModule* bridge = g_module_open(atk_bridge_path.value().c_str(), - static_cast<GModuleFlags>(0)); - if (!bridge) { - VLOG(1) << "Unable to open module " << atk_bridge_path.value(); - return; + g_object_unref(client); + + if (error) { + VLOG(1) << "gconf_client_get_bool failed"; + g_error_free(error); + return false; } - // Try to call gnome_accessibility_module_init from libatk-bridge.so. - void (*gnome_accessibility_module_init)(); - if (g_module_symbol(bridge, "gnome_accessibility_module_init", - (gpointer *)&gnome_accessibility_module_init)) { - (*gnome_accessibility_module_init)(); + return is_enabled; +} + +void AtkUtilAuraLinux::FinishAccessibilityInitOnUIThread() { + if (!is_enabled_) { + VLOG(1) << "Will not enable ATK accessibility support."; + return; } + + DCHECK(g_accessibility_module_init); + g_accessibility_module_init(); } -AtkUtilAuraLinux::~AtkUtilAuraLinux() { +#else + +void AtkUtilAuraLinux::CheckIfAccessibilityIsEnabledOnFileThread() { +} + +void AtkUtilAuraLinux::FinishAccessibilityInitOnUIThread() { } +#endif // defined(USE_GCONF) + } // namespace ui diff --git a/chromium/ui/accessibility/platform/atk_util_auralinux.h b/chromium/ui/accessibility/platform/atk_util_auralinux.h index a5bc75d9311..8342a1299c7 100644 --- a/chromium/ui/accessibility/platform/atk_util_auralinux.h +++ b/chromium/ui/accessibility/platform/atk_util_auralinux.h @@ -29,6 +29,14 @@ class AtkUtilAuraLinux { private: friend struct base::DefaultSingletonTraits<AtkUtilAuraLinux>; + + void CheckIfAccessibilityIsEnabledOnFileThread(); + bool CheckPlatformAccessibilitySupportOnFileThread(); + void FinishAccessibilityInitOnUIThread(); + +#if defined(USE_GCONF) + bool is_enabled_; +#endif }; } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node.cc b/chromium/ui/accessibility/platform/ax_platform_node.cc index 8f889458ddb..adfcc1e2ad2 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node.cc @@ -4,12 +4,23 @@ #include "ui/accessibility/platform/ax_platform_node.h" +#include "base/containers/hash_tables.h" +#include "base/lazy_instance.h" #include "build/build_config.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h" namespace ui { +namespace { + +using UniqueIdMap = base::hash_map<int32_t, AXPlatformNode*>; +// Map from each AXPlatformNode's unique id to its instance. +base::LazyInstance<UniqueIdMap> g_unique_id_map = + LAZY_INSTANCE_INITIALIZER; + +} + #if !defined(PLATFORM_HAS_AX_PLATFORM_NODE_IMPL) // static AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { @@ -28,10 +39,39 @@ AXPlatformNode* AXPlatformNode::FromNativeViewAccessible( } #endif -AXPlatformNode::AXPlatformNode() { +// static +int32_t AXPlatformNode::GetNextUniqueId() { + static int32_t next_unique_id = 1; + int32_t unique_id = next_unique_id; + if (next_unique_id == INT32_MAX) + next_unique_id = 1; + else + next_unique_id++; + + return unique_id; +} + +AXPlatformNode::AXPlatformNode() : unique_id_(GetNextUniqueId()) { + g_unique_id_map.Get()[unique_id_] = this; } 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; + + return nullptr; } } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node.h b/chromium/ui/accessibility/platform/ax_platform_node.h index 1d0ed510b38..c9df25947dd 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node.h +++ b/chromium/ui/accessibility/platform/ax_platform_node.h @@ -46,9 +46,17 @@ class AX_EXPORT AXPlatformNode { static AXPlatformNode* FromNativeViewAccessible( gfx::NativeViewAccessible accessible); + // Each platform accessibility object has a unique id that's guaranteed + // to be a positive number. (It's stored in an int32_t as opposed to + // uint32_t because some platforms want to negate it, so we want to ensure + // the range is below the signed int max.) This can be used when the + // id has to be unique across multiple frames, since node ids are + // only unique within one tree. + static int32_t GetNextUniqueId(); + // Call Destroy rather than deleting this, because the subclass may // use reference counting. - virtual void Destroy() = 0; + virtual void Destroy(); // Get the platform-specific accessible object type for this instance. // On some platforms this is just a type cast, on others it may be a @@ -65,6 +73,10 @@ class AX_EXPORT AXPlatformNode { protected: AXPlatformNode(); virtual ~AXPlatformNode(); + + AXPlatformNode* GetFromUniqueId(int32_t unique_id); + + int32_t unique_id_; }; } // 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 801c3473e66..04e96c02ba5 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc @@ -448,14 +448,15 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDED); if (state & (1 << ui::AX_STATE_FOCUSABLE)) atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSABLE); - if (state & (1 << ui::AX_STATE_FOCUSED)) - atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSED); if (state & (1 << ui::AX_STATE_PRESSED)) atk_state_set_add_state(atk_state_set, ATK_STATE_PRESSED); if (state & (1 << ui::AX_STATE_SELECTABLE)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTABLE); if (state & (1 << ui::AX_STATE_SELECTED)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTED); + + if (delegate_->GetFocus() == GetNativeViewAccessible()) + atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSED); } void AXPlatformNodeAuraLinux::GetAtkRelations(AtkRelationSet* atk_relation_set) diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base.cc b/chromium/ui/accessibility/platform/ax_platform_node_base.cc index 2c1bf90507d..4937e4d3178 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_base.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_base.cc @@ -44,6 +44,7 @@ gfx::NativeViewAccessible AXPlatformNodeBase::ChildAtIndex(int index) { // AXPlatformNode overrides. void AXPlatformNodeBase::Destroy() { + AXPlatformNode::Destroy(); delegate_ = nullptr; delete this; } @@ -92,7 +93,10 @@ bool AXPlatformNodeBase::IsDescendant(AXPlatformNodeBase* node) { return false; if (node == this) return true; - AXPlatformNodeBase* parent = FromNativeViewAccessible(node->GetParent()); + gfx::NativeViewAccessible native_parent = node->GetParent(); + if (!native_parent) + return false; + AXPlatformNodeBase* parent = FromNativeViewAccessible(native_parent); return IsDescendant(parent); } diff --git a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm index a4384f9ac77..c4b54d421bd 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm +++ b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm @@ -24,6 +24,7 @@ typedef std::map<ui::AXRole, NSString*> RoleMap; RoleMap BuildRoleMap() { const MapEntry roles[] = { + {ui::AX_ROLE_ABBR, NSAccessibilityGroupRole}, {ui::AX_ROLE_ALERT, NSAccessibilityGroupRole}, {ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole}, {ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole}, @@ -55,6 +56,7 @@ RoleMap BuildRoleMap() { {ui::AX_ROLE_DISCLOSURE_TRIANGLE, NSAccessibilityDisclosureTriangleRole}, {ui::AX_ROLE_DIV, NSAccessibilityGroupRole}, {ui::AX_ROLE_DOCUMENT, NSAccessibilityGroupRole}, + {ui::AX_ROLE_EMBEDDED_OBJECT, NSAccessibilityGroupRole}, {ui::AX_ROLE_FIGCAPTION, NSAccessibilityGroupRole}, {ui::AX_ROLE_FIGURE, NSAccessibilityGroupRole}, {ui::AX_ROLE_FOOTER, NSAccessibilityGroupRole}, diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.cc b/chromium/ui/accessibility/platform/ax_platform_node_win.cc index ee7a4f9f8c6..c8f7d5445c3 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.cc @@ -78,35 +78,36 @@ namespace ui { namespace { -typedef base::hash_map<LONG, AXPlatformNodeWin*> UniqueIdWinMap; -// Map from each AXPlatformNodeWin's unique id to its instance. -base::LazyInstance<UniqueIdWinMap> g_unique_id_win_map = - LAZY_INSTANCE_INITIALIZER; - typedef base::hash_set<AXPlatformNodeWin*> AXPlatformNodeWinSet; // Set of all AXPlatformNodeWin objects that were the target of an // alert event. base::LazyInstance<AXPlatformNodeWinSet> g_alert_targets = LAZY_INSTANCE_INITIALIZER; -LONG GetNextNegativeUniqueIdForWinAccessibility(AXPlatformNodeWin* obj) { - static LONG next_unique_id = -1; - LONG unique_id = next_unique_id; - if (next_unique_id == LONG_MIN) - next_unique_id = -1; - else - next_unique_id--; +base::LazyInstance<base::ObserverList<IAccessible2UsageObserver>> + g_iaccessible2_usage_observer_list = LAZY_INSTANCE_INITIALIZER; + +} // namespace - g_unique_id_win_map.Get().insert(std::make_pair(unique_id, obj)); +// +// IAccessible2UsageObserver +// - return unique_id; +IAccessible2UsageObserver::IAccessible2UsageObserver() { } -void UnregisterNegativeUniqueId(LONG unique_id) { - g_unique_id_win_map.Get().erase(unique_id); +IAccessible2UsageObserver::~IAccessible2UsageObserver() { } -} // namespace +// static +base::ObserverList<IAccessible2UsageObserver>& + GetIAccessible2UsageObserverList() { + return g_iaccessible2_usage_observer_list.Get(); +} + +// +// AXPlatformNode::Create +// // static AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { @@ -129,8 +130,11 @@ AXPlatformNode* AXPlatformNode::FromNativeViewAccessible( return ax_platform_node.get(); } -AXPlatformNodeWin::AXPlatformNodeWin() - : unique_id_win_(GetNextNegativeUniqueIdForWinAccessibility(this)) { +// +// AXPlatformNodeWin +// + +AXPlatformNodeWin::AXPlatformNodeWin() { } AXPlatformNodeWin::~AXPlatformNodeWin() { @@ -143,7 +147,6 @@ AXPlatformNodeWin::~AXPlatformNodeWin() { void AXPlatformNodeWin::Destroy() { delegate_ = nullptr; - UnregisterNegativeUniqueId(unique_id_win_); RemoveAlertTarget(); Release(); } @@ -157,11 +160,17 @@ void AXPlatformNodeWin::NotifyAccessibilityEvent(ui::AXEvent event_type) { 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; + int native_event = MSAAEvent(event_type); if (native_event < EVENT_MIN) return; - ::NotifyWinEvent(native_event, hwnd, OBJID_CLIENT, unique_id_win_); + ::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) @@ -324,25 +333,28 @@ STDMETHODIMP AXPlatformNodeWin::get_accChild(VARIANT var_child, // that want to enumerate all immediate children. *disp_child = delegate_->ChildAtIndex(child_id - 1); if (!(*disp_child)) - return E_FAIL; + return E_INVALIDARG; (*disp_child)->AddRef(); return S_OK; } if (child_id >= 0) - return E_FAIL; + return E_INVALIDARG; // Negative child ids can be used to map to any descendant. - UniqueIdWinMap* unique_ids = g_unique_id_win_map.Pointer(); - auto iter = unique_ids->find(child_id); - if (iter != unique_ids->end()) { - *disp_child = iter->second; + AXPlatformNodeWin* child = static_cast<AXPlatformNodeWin*>( + GetFromUniqueId(-child_id)); + if (child && !IsDescendant(child)) + child = nullptr; + + if (child) { + *disp_child = child; (*disp_child)->AddRef(); return S_OK; } *disp_child = nullptr; - return E_FAIL; + return E_INVALIDARG; } STDMETHODIMP AXPlatformNodeWin::get_accChildCount(LONG* child_count) { @@ -498,7 +510,7 @@ STDMETHODIMP AXPlatformNodeWin::get_states(AccessibleStates* states) { STDMETHODIMP AXPlatformNodeWin::get_uniqueID(LONG* unique_id) { COM_OBJECT_VALIDATE_1_ARG(unique_id); - *unique_id = unique_id_win_; + *unique_id = -unique_id_; return S_OK; } @@ -876,6 +888,13 @@ STDMETHODIMP AXPlatformNodeWin::scrollSubstringToPoint( STDMETHODIMP AXPlatformNodeWin::QueryService( REFGUID guidService, REFIID riid, void** object) { COM_OBJECT_VALIDATE_1_ARG(object); + + if (riid == IID_IAccessible2) { + FOR_EACH_OBSERVER(IAccessible2UsageObserver, + GetIAccessible2UsageObserverList(), + OnIAccessible2Used()); + } + if (guidService == IID_IAccessible || guidService == IID_IAccessible2 || guidService == IID_IAccessible2_2 || @@ -982,8 +1001,6 @@ int AXPlatformNodeWin::MSAAState() { msaa_state |= STATE_SYSTEM_EXPANDED; if (state & (1 << ui::AX_STATE_FOCUSABLE)) msaa_state |= STATE_SYSTEM_FOCUSABLE; - if (state & (1 << ui::AX_STATE_FOCUSED)) - msaa_state |= STATE_SYSTEM_FOCUSED; if (state & (1 << ui::AX_STATE_HASPOPUP)) msaa_state |= STATE_SYSTEM_HASPOPUP; if (state & (1 << ui::AX_STATE_HOVERED)) @@ -1007,6 +1024,21 @@ int AXPlatformNodeWin::MSAAState() { if (state & (1 << ui::AX_STATE_DISABLED)) msaa_state |= STATE_SYSTEM_UNAVAILABLE; + gfx::NativeViewAccessible focus = delegate_->GetFocus(); + if (focus == GetNativeViewAccessible()) + msaa_state |= STATE_SYSTEM_FOCUSED; + + // On Windows, the "focus" bit should be set on certain containers, like + // menu bars, when visible. + // + // 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 (GetData().role == ui::AX_ROLE_MENU_BAR && + !(state & (1 << ui::AX_STATE_INVISIBLE))) { + msaa_state |= STATE_SYSTEM_FOCUSED; + } + return msaa_state; } diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.h b/chromium/ui/accessibility/platform/ax_platform_node_win.h index 5d264211b34..eb747ed4d0b 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.h @@ -9,11 +9,29 @@ #include <atlcom.h> #include <oleacc.h> +#include "base/observer_list.h" #include "third_party/iaccessible2/ia2_api_all.h" +#include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_text_utils.h" #include "ui/accessibility/platform/ax_platform_node_base.h" namespace ui { +// A simple interface for a class that wants to be notified when IAccessible2 +// is used by a client, a strong indication that full accessibility support +// should be enabled. +class AX_EXPORT IAccessible2UsageObserver { + public: + IAccessible2UsageObserver(); + virtual ~IAccessible2UsageObserver(); + virtual void OnIAccessible2Used() = 0; +}; + +// Get an observer list that allows modules across the codebase to +// listen to when usage of IAccessible2 is detected. +extern AX_EXPORT base::ObserverList<IAccessible2UsageObserver>& + GetIAccessible2UsageObserverList(); + class __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) AXPlatformNodeWin : public CComObjectRootEx<CComMultiThreadModel>, @@ -279,16 +297,6 @@ AXPlatformNodeWin IA2TextBoundaryType ia2_boundary, LONG start_offset, ui::TextBoundaryDirection direction); - - // A windows-specific unique ID for this object. It's returned in - // IAccessible2::get_uniqueID, but more importantly it's used for - // firing events. On Windows, we fire events on the nearest parent HWND - // and pass the unique ID as the child id parameter. When the client - // wants to retrieve the object the event was fired on, it calls - // get_accChild and passes the child ID. We use negative IDs for the unique - // ID so we can distinguish a request for an arbitrary child from a request - // for an immediate child of an object by its 0-based index. - LONG unique_id_win_; }; } // 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 08be59b21f1..d5a4f81e501 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc @@ -315,7 +315,8 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { // Asking for child id 3 should fail. ScopedComPtr<IDispatch> result; ScopedVariant child3(3); - ASSERT_EQ(E_FAIL, root_iaccessible->get_accChild(child3, result.Receive())); + ASSERT_EQ(E_INVALIDARG, + root_iaccessible->get_accChild(child3, result.Receive())); } // We should be able to ask for the button by its unique id too. @@ -332,6 +333,20 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { ASSERT_EQ(result.get(), button_iaccessible); } + // We shouldn't be able to ask for the root node by its unique ID + // from one of its children, though. + LONG root_unique_id; + ScopedComPtr<IAccessible2> root_iaccessible2 = + ToIAccessible2(root_iaccessible); + root_iaccessible2->get_uniqueID(&root_unique_id); + ASSERT_LT(root_unique_id, 0); + { + ScopedComPtr<IDispatch> result; + ScopedVariant root_id_variant(root_unique_id); + ASSERT_EQ(E_INVALIDARG, button_iaccessible->get_accChild(root_id_variant, + result.Receive())); + } + // Now check parents. { ScopedComPtr<IDispatch> result; diff --git a/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc b/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc index fbb76ca13be..f2b91731aa4 100644 --- a/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc +++ b/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc @@ -18,6 +18,9 @@ gfx::Vector2d g_offset; // A simple implementation of AXTreeDelegate to catch when AXNodes are // deleted so we can delete their wrappers. class TestAXTreeDelegate : public AXTreeDelegate { + void OnNodeDataWillChange(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) override {} void OnTreeDataChanged(AXTree* tree) override {} void OnNodeWillBeDeleted(AXTree* tree, AXNode* node) override { auto iter = g_node_to_wrapper_map.find(node); diff --git a/chromium/ui/android/BUILD.gn b/chromium/ui/android/BUILD.gn index 85a4e92b4ee..fc4c55047a7 100644 --- a/chromium/ui/android/BUILD.gn +++ b/chromium/ui/android/BUILD.gn @@ -34,7 +34,6 @@ component("android") { "view_android.h", "window_android.cc", "window_android.h", - "window_android_compositor.cc", "window_android_compositor.h", "window_android_observer.h", ] @@ -131,7 +130,76 @@ android_resources("ui_java_resources") { } android_library("ui_java") { - DEPRECATED_java_in_dir = "java/src" + java_files = [ + "java/src/org/chromium/ui/ColorPickerAdvanced.java", + "java/src/org/chromium/ui/ColorPickerAdvancedComponent.java", + "java/src/org/chromium/ui/ColorPickerDialog.java", + "java/src/org/chromium/ui/ColorPickerMoreButton.java", + "java/src/org/chromium/ui/ColorPickerSimple.java", + "java/src/org/chromium/ui/ColorSuggestion.java", + "java/src/org/chromium/ui/ColorSuggestionListAdapter.java", + "java/src/org/chromium/ui/DropdownAdapter.java", + "java/src/org/chromium/ui/DropdownDividerDrawable.java", + "java/src/org/chromium/ui/DropdownItem.java", + "java/src/org/chromium/ui/DropdownPopupWindow.java", + "java/src/org/chromium/ui/OnColorChangedListener.java", + "java/src/org/chromium/ui/UiUtils.java", + "java/src/org/chromium/ui/VSyncMonitor.java", + "java/src/org/chromium/ui/autofill/AutofillDelegate.java", + "java/src/org/chromium/ui/autofill/AutofillKeyboardAccessory.java", + "java/src/org/chromium/ui/autofill/AutofillPopup.java", + "java/src/org/chromium/ui/autofill/AutofillSuggestion.java", + "java/src/org/chromium/ui/base/ActivityWindowAndroid.java", + "java/src/org/chromium/ui/base/AndroidPermissionDelegate.java", + "java/src/org/chromium/ui/base/Clipboard.java", + "java/src/org/chromium/ui/base/DeviceFormFactor.java", + "java/src/org/chromium/ui/base/LocalizationUtils.java", + "java/src/org/chromium/ui/base/ResourceBundle.java", + "java/src/org/chromium/ui/base/SelectFileDialog.java", + "java/src/org/chromium/ui/base/TouchDevice.java", + "java/src/org/chromium/ui/base/ViewAndroidDelegate.java", + "java/src/org/chromium/ui/base/WindowAndroid.java", + "java/src/org/chromium/ui/gfx/BitmapHelper.java", + "java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java", + "java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java", + "java/src/org/chromium/ui/gl/SurfaceTextureListener.java", + "java/src/org/chromium/ui/gl/SurfaceTexturePlatformWrapper.java", + "java/src/org/chromium/ui/interpolators/BakedBezierInterpolator.java", + "java/src/org/chromium/ui/picker/ChromeDatePickerDialog.java", + "java/src/org/chromium/ui/picker/DateDialogNormalizer.java", + "java/src/org/chromium/ui/picker/DateTimePickerDialog.java", + "java/src/org/chromium/ui/picker/DateTimeSuggestion.java", + "java/src/org/chromium/ui/picker/DateTimeSuggestionListAdapter.java", + "java/src/org/chromium/ui/picker/InputDialogContainer.java", + "java/src/org/chromium/ui/picker/MonthPicker.java", + "java/src/org/chromium/ui/picker/MonthPickerDialog.java", + "java/src/org/chromium/ui/picker/MultiFieldTimePickerDialog.java", + "java/src/org/chromium/ui/picker/TwoFieldDatePicker.java", + "java/src/org/chromium/ui/picker/TwoFieldDatePickerDialog.java", + "java/src/org/chromium/ui/picker/WeekPicker.java", + "java/src/org/chromium/ui/picker/WeekPickerDialog.java", + "java/src/org/chromium/ui/resources/LayoutResource.java", + "java/src/org/chromium/ui/resources/Resource.java", + "java/src/org/chromium/ui/resources/ResourceLoader.java", + "java/src/org/chromium/ui/resources/ResourceManager.java", + "java/src/org/chromium/ui/resources/async/AsyncPreloadResourceLoader.java", + "java/src/org/chromium/ui/resources/dynamics/BitmapDynamicResource.java", + "java/src/org/chromium/ui/resources/dynamics/DynamicResource.java", + "java/src/org/chromium/ui/resources/dynamics/DynamicResourceLoader.java", + "java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java", + "java/src/org/chromium/ui/resources/dynamics/ViewResourceInflater.java", + "java/src/org/chromium/ui/resources/sprites/CrushedSpriteResource.java", + "java/src/org/chromium/ui/resources/sprites/CrushedSpriteResourceLoader.java", + "java/src/org/chromium/ui/resources/statics/NinePatchData.java", + "java/src/org/chromium/ui/resources/statics/StaticResource.java", + "java/src/org/chromium/ui/resources/statics/StaticResourceLoader.java", + "java/src/org/chromium/ui/resources/system/SystemResourceLoader.java", + "java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java", + "java/src/org/chromium/ui/text/SpanApplier.java", + "java/src/org/chromium/ui/widget/ButtonCompat.java", + "java/src/org/chromium/ui/widget/TextViewWithClickableSpans.java", + "java/src/org/chromium/ui/widget/Toast.java", + ] deps = [ ":ui_java_resources", "//base:base_java", @@ -140,7 +208,10 @@ android_library("ui_java") { } android_library("ui_javatests") { - DEPRECATED_java_in_dir = "javatests/src" + java_files = [ + "javatests/src/org/chromium/ui/picker/DateTimePickerDialogTest.java", + "javatests/src/org/chromium/ui/resources/sprites/CrushedSpriteResourceTest.java", + ] testonly = true deps = [ ":ui_java", @@ -150,6 +221,19 @@ android_library("ui_javatests") { ] } +# GYP: //ui/android/ui_android.gyp:ui_junit_tests +junit_binary("ui_junit_tests") { + java_files = [ + "junit/src/org/chromium/ui/picker/DateDialogNormalizerTest.java", + "junit/src/org/chromium/ui/text/SpanApplierTest.java", + ] + deps = [ + "//base:base_java", + "//base:base_junit_test_support", + "//ui/android:ui_java", + ] +} + test("ui_android_unittests") { sources = [ "overscroll_refresh_unittest.cc", diff --git a/chromium/ui/android/edge_effect.cc b/chromium/ui/android/edge_effect.cc index 5796c023d75..a5189405681 100644 --- a/chromium/ui/android/edge_effect.cc +++ b/chromium/ui/android/edge_effect.cc @@ -65,8 +65,7 @@ class EdgeEffect::EffectLayer { public: EffectLayer(ui::SystemUIResourceType resource_type, ui::ResourceManager* resource_manager) - : ui_resource_layer_(cc::UIResourceLayer::Create( - WindowAndroidCompositor::LayerSettings())), + : ui_resource_layer_(cc::UIResourceLayer::Create()), resource_type_(resource_type), resource_manager_(resource_manager) {} diff --git a/chromium/ui/android/edge_effect_l.cc b/chromium/ui/android/edge_effect_l.cc index 3a19775d27a..305ca4971de 100644 --- a/chromium/ui/android/edge_effect_l.cc +++ b/chromium/ui/android/edge_effect_l.cc @@ -51,8 +51,7 @@ const ui::SystemUIResourceType kResourceId = ui::OVERSCROLL_GLOW_L; EdgeEffectL::EdgeEffectL(ui::ResourceManager* resource_manager) : resource_manager_(resource_manager), - glow_(cc::UIResourceLayer::Create( - WindowAndroidCompositor::LayerSettings())), + glow_(cc::UIResourceLayer::Create()), glow_alpha_(0), glow_scale_y_(0), glow_alpha_start_(0), diff --git a/chromium/ui/android/overscroll_glow.cc b/chromium/ui/android/overscroll_glow.cc index 3f9fb02df26..7f3c43da306 100644 --- a/chromium/ui/android/overscroll_glow.cc +++ b/chromium/ui/android/overscroll_glow.cc @@ -194,7 +194,7 @@ bool OverscrollGlow::InitializeIfNecessary() { return true; DCHECK(!root_layer_.get()); - root_layer_ = cc::Layer::Create(WindowAndroidCompositor::LayerSettings()); + root_layer_ = cc::Layer::Create(); for (size_t i = 0; i < EDGE_COUNT; ++i) { edge_effects_[i] = client_->CreateEdgeEffect(); DCHECK(edge_effects_[i]); diff --git a/chromium/ui/android/resources/resource_manager_impl_unittest.cc b/chromium/ui/android/resources/resource_manager_impl_unittest.cc index 649ce7be5c5..957b2a14e92 100644 --- a/chromium/ui/android/resources/resource_manager_impl_unittest.cc +++ b/chromium/ui/android/resources/resource_manager_impl_unittest.cc @@ -97,7 +97,7 @@ class ResourceManagerTest : public testing::Test { params.settings = &settings; params.task_graph_runner = &task_graph_runner_; host_.reset(new MockLayerTreeHost(¶ms, - cc::CompositorMode::SingleThreaded)); + cc::CompositorMode::SINGLE_THREADED)); resource_manager_.Init(host_.get()); } diff --git a/chromium/ui/android/ui_android.gyp b/chromium/ui/android/ui_android.gyp index b1d2857afb7..c818edb82f6 100644 --- a/chromium/ui/android/ui_android.gyp +++ b/chromium/ui/android/ui_android.gyp @@ -47,7 +47,6 @@ 'view_android.h', 'window_android.cc', 'window_android.h', - 'window_android_compositor.cc', 'window_android_compositor.h', 'window_android_observer.h', ], @@ -167,6 +166,29 @@ ], }, { + # GN: //ui/android:ui_junit_tests + 'target_name': 'ui_junit_tests', + 'type': 'none', + 'dependencies': [ + 'ui_java', + '../../base/base.gyp:base', + '../../base/base.gyp:base_junit_test_support', + '../../testing/android/junit/junit_test.gyp:junit_test_support', + ], + 'variables': { + 'main_class': 'org.chromium.testing.local.JunitTestMain', + 'src_paths': [ + 'junit/', + ], + 'test_type': 'junit', + 'wrapper_script_name': 'helper/<(_target_name)', + }, + 'includes': [ + '../../build/android/test_runner.gypi', + '../../build/host_jar.gypi', + ], + }, + { # GN version: //ui/android:ui_android_unittests 'target_name': 'ui_android_unittests', 'type': '<(gtest_target_type)', diff --git a/chromium/ui/android/window_android_compositor.cc b/chromium/ui/android/window_android_compositor.cc deleted file mode 100644 index 3fb53ead0d6..00000000000 --- a/chromium/ui/android/window_android_compositor.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -#include "ui/android/window_android_compositor.h" - -#include "base/lazy_instance.h" -#include "cc/layers/layer_settings.h" - -namespace ui { - -namespace { -base::LazyInstance<cc::LayerSettings> g_layer_settings = - LAZY_INSTANCE_INITIALIZER; -} // anonymous namespace - -// static -const cc::LayerSettings& WindowAndroidCompositor::LayerSettings() { - return g_layer_settings.Get(); -} - -// static -void WindowAndroidCompositor::SetLayerSettings( - const cc::LayerSettings& settings) { - g_layer_settings.Get() = settings; -} - -} // namespace ui diff --git a/chromium/ui/android/window_android_compositor.h b/chromium/ui/android/window_android_compositor.h index 51627d0d169..a6ffb336723 100644 --- a/chromium/ui/android/window_android_compositor.h +++ b/chromium/ui/android/window_android_compositor.h @@ -10,7 +10,6 @@ namespace cc { class Layer; -class LayerSettings; } namespace ui { @@ -20,12 +19,8 @@ class ResourceManager; // Android interface for compositor-related tasks. class UI_ANDROID_EXPORT WindowAndroidCompositor { public: - static const cc::LayerSettings& LayerSettings(); - static void SetLayerSettings(const cc::LayerSettings& settings); - virtual ~WindowAndroidCompositor() {} - virtual void AttachLayerForReadback(scoped_refptr<cc::Layer> layer) = 0; virtual void RequestCopyOfOutputOnRootLayer( scoped_ptr<cc::CopyOutputRequest> request) = 0; virtual void OnVSync(base::TimeTicks frame_time, diff --git a/chromium/ui/app_list/app_list.gyp b/chromium/ui/app_list/app_list.gyp index 5bbfc77e8f1..e8d42112297 100644 --- a/chromium/ui/app_list/app_list.gyp +++ b/chromium/ui/app_list/app_list.gyp @@ -317,17 +317,6 @@ ['exclude', 'cocoa/'], ], }], - # See http://crbug.com/162998#c4 for why this is needed. - ['OS=="linux" and use_allocator!="none"', { - 'dependencies': [ - '../../base/allocator/allocator.gyp:allocator', - ], - }], - ['OS=="win" and win_use_allocator_shim==1', { - 'dependencies': [ - '../../base/allocator/allocator.gyp:allocator', - ], - }], ], # Disable c4267 warnings until we fix size_t to int truncations. 'msvs_disabled_warnings': [ 4267, ], @@ -370,11 +359,6 @@ '../../content/content.gyp:sandbox_helper_win', ], }], - ['OS=="win" and component!="shared_library" and win_use_allocator_shim==1', { - 'dependencies': [ - '<(DEPTH)/base/allocator/allocator.gyp:allocator', - ], - }], ], }, ], diff --git a/chromium/ui/app_list/shower/app_list_shower.gyp b/chromium/ui/app_list/shower/app_list_shower.gyp new file mode 100644 index 00000000000..1d342622265 --- /dev/null +++ b/chromium/ui/app_list/shower/app_list_shower.gyp @@ -0,0 +1,107 @@ +# 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + # GN version: //ui/app_list/shower + 'target_name': 'app_list_shower', + 'type': '<(component)', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../skia/skia.gyp:skia', + '../../aura/aura.gyp:aura', + '../../compositor/compositor.gyp:compositor', + '../../events/events.gyp:events_base', + '../../events/events.gyp:events', + '../../gfx/gfx.gyp:gfx_geometry', + '../../views/views.gyp:views', + '../app_list.gyp:app_list', + ], + 'defines': [ + 'APP_LIST_SHOWER_IMPLEMENTATION', + ], + 'sources': [ + # Note: sources list duplicated in GN build. + 'app_list_shower.h', + 'app_list_shower_delegate.h', + 'app_list_shower_delegate_factory.h', + 'app_list_shower_export.h', + 'app_list_shower_impl.cc', + 'app_list_shower_impl.h', + ], + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + }, + { + # GN version: //ui/app_list/shower:test_support + 'target_name': 'app_list_shower_test_support', + 'type': 'static_library', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../skia/skia.gyp:skia', + 'app_list_shower', + ], + 'sources': [ + # Note: sources list duplicated in GN build. + 'test/app_list_shower_impl_test_api.cc', + 'test/app_list_shower_impl_test_api.h', + ], + }, + { + # GN version: //ui/app_list/shower:app_list_shower_unittests + 'target_name': 'app_list_shower_unittests', + 'type': 'executable', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../base/base.gyp:test_support_base', + '../../../skia/skia.gyp:skia', + '../../../testing/gtest.gyp:gtest', + '../../aura/aura.gyp:aura_test_support', + '../../resources/ui_resources.gyp:ui_test_pak', + '../../views/views.gyp:views', + '../../wm/wm.gyp:wm', + '../app_list.gyp:app_list_test_support', + 'app_list_shower', + 'app_list_shower_test_support', + ], + 'sources': [ + # Note: sources list duplicated in GN build. + 'app_list_shower_impl_unittest.cc', + 'test/run_all_unittests.cc', + ], + # Disable c4267 warnings until we fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + }, + ], + 'conditions': [ + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'app_list_shower_unittests_run', + 'type': 'none', + 'dependencies': [ + 'app_list_shower_unittests', + ], + 'includes': [ + '../../../build/isolate.gypi', + ], + 'sources': [ + 'app_list_shower_unittests.isolate', + ], + 'conditions': [ + ['use_x11 == 1', { + 'dependencies': [ + '../../../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck', + ], + }], + ], + }, + ], + }], + ], +} diff --git a/chromium/ui/app_list/shower/app_list_shower_unittests.isolate b/chromium/ui/app_list/shower/app_list_shower_unittests.isolate new file mode 100644 index 00000000000..2a89e3e7264 --- /dev/null +++ b/chromium/ui/app_list/shower/app_list_shower_unittests.isolate @@ -0,0 +1,78 @@ +# Copyright (c) 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. +{ + 'conditions': [ + ['use_x11==0', { + 'variables': { + 'command': [ + '../../../testing/test_env.py', + '<(PRODUCT_DIR)/app_list_shower_unittests<(EXECUTABLE_SUFFIX)', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + '--asan=<(asan)', + '--msan=<(msan)', + '--tsan=<(tsan)', + ], + }, + }], + ['use_x11==1', { + 'variables': { + 'command': [ + '../../../testing/xvfb.py', + '<(PRODUCT_DIR)', + '<(PRODUCT_DIR)/app_list_shower_unittests<(EXECUTABLE_SUFFIX)', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + '--asan=<(asan)', + '--msan=<(msan)', + '--tsan=<(tsan)', + ], + 'files': [ + '../../../testing/xvfb.py', + '<(PRODUCT_DIR)/xdisplaycheck<(EXECUTABLE_SUFFIX)', + ], + }, + }], + ['OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'files': [ + '../../../testing/test_env.py', + '<(PRODUCT_DIR)/ui_test.pak', + ], + }, + }], + ['OS=="linux"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/libosmesa.so', + ], + }, + }], + ['OS=="mac"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/osmesa.so', + ], + }, + }], + ['OS=="win"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/osmesa.dll', + ], + }, + }], + ['OS=="mac" and asan==1 and fastbuild==0', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/app_list_shower_unittests.dSYM/', + '<(PRODUCT_DIR)/osmesa.so.dSYM/', + ], + }, + }], + ], + 'includes': [ + '../../../base/base.isolate', + ], +} diff --git a/chromium/ui/arc/BUILD.gn b/chromium/ui/arc/BUILD.gn index 76a4b95e5f3..7b987aa7d9b 100644 --- a/chromium/ui/arc/BUILD.gn +++ b/chromium/ui/arc/BUILD.gn @@ -21,3 +21,21 @@ static_library("arc") { "//ui/message_center", ] } + +static_library("ui_arc_unittests") { + testonly = true + sources = [ + "notification/arc_notification_manager_unittest.cc", + "test/run_all_unittests.cc", + ] + + deps = [ + ":arc", + "//base", + "//base/test:test_support", + "//components/arc:arc_test_support", + "//mojo/edk/system", + "//mojo/message_pump", + "//ui/message_center:test_support", + ] +} diff --git a/chromium/ui/arc/DEPS b/chromium/ui/arc/DEPS index ea22bc1561d..b692d8152c9 100644 --- a/chromium/ui/arc/DEPS +++ b/chromium/ui/arc/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+ash", "+components/arc", "+mojo", ] diff --git a/chromium/ui/arc/arc.gyp b/chromium/ui/arc/arc.gyp index b0789e13137..e1a40a766f9 100644 --- a/chromium/ui/arc/arc.gyp +++ b/chromium/ui/arc/arc.gyp @@ -8,15 +8,17 @@ }, 'targets': [ { + # GN: //ui/arc:arc 'target_name': 'arc', 'type': 'static_library', 'include_dirs': [ - '..', + '../..', ], 'dependencies': [ '../gfx/gfx.gyp:gfx_geometry', '../message_center/message_center.gyp:message_center', '../../base/base.gyp:base', + '../../url/url.gyp:url_lib', '../../skia/skia.gyp:skia', '../../components/components.gyp:arc_mojo_bindings', '../../components/components.gyp:signin_core_account_id', @@ -28,5 +30,28 @@ 'notification/arc_notification_item.h', ], }, + { + # GN: //ui/arc:ui_arc_unittests + 'target_name': 'ui_arc_unittests', + 'type': '<(gtest_target_type)', + 'include_dirs': [ + '../..', + ], + 'dependencies': [ + '../../base/base.gyp:base', + '../../base/base.gyp:test_support_base', + '../../components/components.gyp:arc_test_support', + '../../mojo/mojo_edk.gyp:mojo_system_impl', + '../../mojo/mojo_public.gyp:mojo_cpp_bindings', + '../../mojo/mojo_public.gyp:mojo_message_pump_lib', + '../../testing/gtest.gyp:gtest', + '../message_center/message_center.gyp:message_center_test_support', + 'arc', + ], + 'sources': [ + 'notification/arc_notification_manager_unittest.cc', + 'test/run_all_unittests.cc', + ], + }, ], } diff --git a/chromium/ui/arc/notification/arc_notification_item.cc b/chromium/ui/arc/notification/arc_notification_item.cc index 05e67ab07ee..f0d791bbe90 100644 --- a/chromium/ui/arc/notification/arc_notification_item.cc +++ b/chromium/ui/arc/notification/arc_notification_item.cc @@ -60,6 +60,11 @@ class ArcNotificationDelegate : public message_center::NotificationDelegate { item_->Click(); } + void ButtonClick(int button_index) override { + if (item_) + item_->ButtonClick(button_index); + } + private: // The destructor is private since this class is ref-counted. ~ArcNotificationDelegate() override {} @@ -104,15 +109,15 @@ void ArcNotificationItem::UpdateWithArcNotificationData( message_center::NotificationType type; switch (data.type) { - case ARC_NOTIFICATION_TYPE_BASIC: - type = message_center::NOTIFICATION_TYPE_SIMPLE; + case ArcNotificationType::BASIC: + type = message_center::NOTIFICATION_TYPE_BASE_FORMAT; break; - case ARC_NOTIFICATION_TYPE_IMAGE: + case ArcNotificationType::IMAGE: // TODO(yoshiki): Implement this types. - type = message_center::NOTIFICATION_TYPE_SIMPLE; + type = message_center::NOTIFICATION_TYPE_BASE_FORMAT; LOG(ERROR) << "Unsupported notification type: image"; break; - case ARC_NOTIFICATION_TYPE_PROGRESS: + case ArcNotificationType::PROGRESS: type = message_center::NOTIFICATION_TYPE_PROGRESS; rich_data.timestamp = base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(data.time); @@ -122,8 +127,17 @@ void ArcNotificationItem::UpdateWithArcNotificationData( data.progress_max * 100)))); break; } - DCHECK(0 <= data.type && data.type <= ARC_NOTIFICATION_TYPE_MAX) - << "Unsupported notification type: " << data.type; + DCHECK(IsKnownEnumValue(data.type)) << "Unsupported notification type: " + << data.type; + + for (size_t i = 0; i < data.buttons.size(); i++) { + rich_data.buttons.push_back(message_center::ButtonInfo( + base::UTF8ToUTF16(data.buttons.at(i)->label.get()))); + } + + // If the client is old (version < 1), both |no_clear| and |ongoing_event| + // are false. + rich_data.pinned = (data.no_clear || data.ongoing_event); // The identifier of the notifier, which is used to distinguish the notifiers // in the message center. @@ -158,9 +172,9 @@ void ArcNotificationItem::UpdateWithArcNotificationData( ArcNotificationItem::~ArcNotificationItem() {} -void ArcNotificationItem::OnClosedFromAndroid() { +void ArcNotificationItem::OnClosedFromAndroid(bool by_user) { being_removed_by_manager_ = true; // Closing is initiated by the manager. - message_center_->RemoveNotification(notification_id_, true /* by_user */); + message_center_->RemoveNotification(notification_id_, by_user); } void ArcNotificationItem::Close(bool by_user) { @@ -179,6 +193,11 @@ void ArcNotificationItem::Click() { manager_->SendNotificationClickedOnChrome(notification_key_); } +void ArcNotificationItem::ButtonClick(int button_index) { + manager_->SendNotificationButtonClickedOnChrome( + notification_key_, button_index); +} + void ArcNotificationItem::OnImageDecoded(const SkBitmap& bitmap) { DCHECK(thread_checker_.CalledOnValidThread()); diff --git a/chromium/ui/arc/notification/arc_notification_item.h b/chromium/ui/arc/notification/arc_notification_item.h index 902471686dd..8d8346ab8a6 100644 --- a/chromium/ui/arc/notification/arc_notification_item.h +++ b/chromium/ui/arc/notification/arc_notification_item.h @@ -30,11 +30,12 @@ class ArcNotificationItem { void UpdateWithArcNotificationData(const ArcNotificationData& data); // Methods called from ArcNotificationManager: - void OnClosedFromAndroid(); + void OnClosedFromAndroid(bool by_user); // Methods called from ArcNotificationItemDelegate: void Close(bool by_user); void Click(); + void ButtonClick(int button_index); private: void OnImageDecoded(const SkBitmap& bitmap); diff --git a/chromium/ui/arc/notification/arc_notification_manager.cc b/chromium/ui/arc/notification/arc_notification_manager.cc index 91b8c269b25..77869328be3 100644 --- a/chromium/ui/arc/notification/arc_notification_manager.cc +++ b/chromium/ui/arc/notification/arc_notification_manager.cc @@ -4,91 +4,184 @@ #include "ui/arc/notification/arc_notification_manager.h" +#include "ash/shell.h" +#include "ash/system/toast/toast_manager.h" #include "base/stl_util.h" #include "ui/arc/notification/arc_notification_item.h" namespace arc { -ArcNotificationManager::ArcNotificationManager(ArcBridgeService* arc_bridge, +ArcNotificationManager::ArcNotificationManager(ArcBridgeService* bridge_service, const AccountId& main_profile_id) - : arc_bridge_(arc_bridge), + : ArcNotificationManager(bridge_service, + main_profile_id, + message_center::MessageCenter::Get()) {} + +ArcNotificationManager::ArcNotificationManager( + ArcBridgeService* bridge_service, + const AccountId& main_profile_id, + message_center::MessageCenter* message_center) + : ArcService(bridge_service), main_profile_id_(main_profile_id), + message_center_(message_center), binding_(this) { - // This must be initialized after ArcBridgeService. - DCHECK(arc_bridge_); - DCHECK_EQ(arc_bridge_, ArcBridgeService::Get()); - arc_bridge_->AddObserver(this); + arc_bridge_service()->AddObserver(this); } ArcNotificationManager::~ArcNotificationManager() { - // This should be free'd before ArcBridgeService. - DCHECK(ArcBridgeService::Get()); - DCHECK_EQ(arc_bridge_, ArcBridgeService::Get()); - arc_bridge_->RemoveObserver(this); + arc_bridge_service()->RemoveObserver(this); } void ArcNotificationManager::OnNotificationsInstanceReady() { - NotificationsInstance* notifications_instance = - arc_bridge_->notifications_instance(); + DCHECK(!ready_); + + auto notifications_instance = arc_bridge_service()->notifications_instance(); if (!notifications_instance) { VLOG(2) << "Request to refresh app list when bridge service is not ready."; return; } - NotificationsHostPtr host; - binding_.Bind(mojo::GetProxy(&host)); - notifications_instance->Init(std::move(host)); + notifications_instance->Init(binding_.CreateInterfacePtrAndBind()); + ready_ = true; +} + +void ArcNotificationManager::OnNotificationsInstanceClosed() { + DCHECK(ready_); + while (!items_.empty()) { + auto it = items_.begin(); + scoped_ptr<ArcNotificationItem> item = std::move(it->second); + items_.erase(it); + item->OnClosedFromAndroid(false /* by_user */); + } + ready_ = false; } void ArcNotificationManager::OnNotificationPosted(ArcNotificationDataPtr data) { - ArcNotificationItem* item = items_.get(data->key); - if (!item) { + const std::string& key = data->key; + auto it = items_.find(key); + if (it == items_.end()) { // Show a notification on the primary loged-in user's desktop. // TODO(yoshiki): Reconsider when ARC supports multi-user. - item = new ArcNotificationItem(this, message_center::MessageCenter::Get(), - data->key, main_profile_id_); - items_.set(data->key, make_scoped_ptr(item)); + ArcNotificationItem* item = + new ArcNotificationItem(this, message_center_, key, main_profile_id_); + // TODO(yoshiki): Use emplacement for performance when it's available. + auto result = items_.insert(std::make_pair(key, make_scoped_ptr(item))); + DCHECK(result.second); + it = result.first; } - item->UpdateWithArcNotificationData(*data); + it->second->UpdateWithArcNotificationData(*data); } void ArcNotificationManager::OnNotificationRemoved(const mojo::String& key) { - ItemMap::iterator it = items_.find(key.get()); + auto it = items_.find(key.get()); if (it == items_.end()) { VLOG(3) << "Android requests to remove a notification (key: " << key << "), but it is already gone."; return; } - scoped_ptr<ArcNotificationItem> item(items_.take_and_erase(it)); - item->OnClosedFromAndroid(); + scoped_ptr<ArcNotificationItem> item = std::move(it->second); + items_.erase(it); + item->OnClosedFromAndroid(true /* by_user */); } void ArcNotificationManager::SendNotificationRemovedFromChrome( const std::string& key) { - ItemMap::iterator it = items_.find(key); + auto it = items_.find(key); if (it == items_.end()) { VLOG(3) << "Chrome requests to remove a notification (key: " << key << "), but it is already gone."; return; } - scoped_ptr<ArcNotificationItem> item(items_.take_and_erase(it)); + // The removed ArcNotificationItem needs to live in this scope, since the + // given argument |key| may be a part of the removed item. + scoped_ptr<ArcNotificationItem> item = std::move(it->second); + items_.erase(it); - arc_bridge_->notifications_instance()->SendNotificationEventToAndroid( - key, ARC_NOTIFICATION_EVENT_CLOSED); + auto notifications_instance = arc_bridge_service()->notifications_instance(); + + // On shutdown, the ARC channel may quit earlier then notifications. + if (!notifications_instance) { + VLOG(2) << "ARC Notification (key: " << key + << ") is closed, but the ARC channel has already gone."; + return; + } + + notifications_instance->SendNotificationEventToAndroid( + key, ArcNotificationEvent::CLOSED); } void ArcNotificationManager::SendNotificationClickedOnChrome( const std::string& key) { - if (!items_.contains(key)) { + if (items_.find(key) == items_.end()) { VLOG(3) << "Chrome requests to fire a click event on notification (key: " << key << "), but it is gone."; return; } - arc_bridge_->notifications_instance()->SendNotificationEventToAndroid( - key, ARC_NOTIFICATION_EVENT_BODY_CLICKED); + auto notifications_instance = arc_bridge_service()->notifications_instance(); + + // On shutdown, the ARC channel may quit earlier then notifications. + if (!notifications_instance) { + VLOG(2) << "ARC Notification (key: " << key + << ") is clicked, but the ARC channel has already gone."; + return; + } + + notifications_instance->SendNotificationEventToAndroid( + key, ArcNotificationEvent::BODY_CLICKED); +} + +void ArcNotificationManager::SendNotificationButtonClickedOnChrome( + const std::string& key, int button_index) { + if (items_.find(key) == items_.end()) { + VLOG(3) << "Chrome requests to fire a click event on notification (key: " + << key << "), but it is gone."; + return; + } + + auto notifications_instance = arc_bridge_service()->notifications_instance(); + + // On shutdown, the ARC channel may quit earlier then notifications. + if (!notifications_instance) { + VLOG(2) << "ARC Notification (key: " << key + << ")'s button is clicked, but the ARC channel has already gone."; + return; + } + + arc::ArcNotificationEvent command; + switch (button_index) { + case 0: + command = ArcNotificationEvent::BUTTON_1_CLICKED; + break; + case 1: + command = ArcNotificationEvent::BUTTON_2_CLICKED; + break; + case 2: + command = ArcNotificationEvent::BUTTON_3_CLICKED; + break; + case 3: + command = ArcNotificationEvent::BUTTON_4_CLICKED; + break; + case 4: + command = ArcNotificationEvent::BUTTON_5_CLICKED; + break; + default: + VLOG(3) << "Invalid button index (key: " << key << ", index: " << + button_index << ")."; + return; + } + + notifications_instance->SendNotificationEventToAndroid(key, command); +} + +void ArcNotificationManager::OnToastPosted(ArcToastDataPtr data) { + ash::Shell::GetInstance()->toast_manager()->Show(data->text, data->duration); +} + +void ArcNotificationManager::OnToastCancelled(ArcToastDataPtr data) { + // TODO(yoshiki): Implement cancel. } } // namespace arc diff --git a/chromium/ui/arc/notification/arc_notification_manager.h b/chromium/ui/arc/notification/arc_notification_manager.h index d197be81e4b..7dfba3412f7 100644 --- a/chromium/ui/arc/notification/arc_notification_manager.h +++ b/chromium/ui/arc/notification/arc_notification_manager.h @@ -5,45 +5,59 @@ #ifndef UI_ARC_NOTIFICATION_ARC_NOTIFICATION_MANAGER_H_ #define UI_ARC_NOTIFICATION_ARC_NOTIFICATION_MANAGER_H_ -#include <map> #include <string> +#include <unordered_map> -#include "base/containers/scoped_ptr_hash_map.h" #include "components/arc/arc_bridge_service.h" +#include "components/arc/arc_service.h" #include "components/arc/common/notifications.mojom.h" #include "components/signin/core/account_id/account_id.h" #include "mojo/public/cpp/bindings/binding.h" +#include "ui/message_center/message_center.h" namespace arc { class ArcNotificationItem; -class ArcNotificationManager : public ArcBridgeService::Observer, +class ArcNotificationManager : public ArcService, + public ArcBridgeService::Observer, public NotificationsHost { public: ArcNotificationManager(ArcBridgeService* bridge_service, const AccountId& main_profile_id); + + ArcNotificationManager(ArcBridgeService* bridge_service, + const AccountId& main_profile_id, + message_center::MessageCenter* message_center); + ~ArcNotificationManager() override; // ArcBridgeService::Observer implementation: void OnNotificationsInstanceReady() override; + void OnNotificationsInstanceClosed() override; // NotificationsHost implementation: void OnNotificationPosted(ArcNotificationDataPtr data) override; void OnNotificationRemoved(const mojo::String& key) override; + void OnToastPosted(ArcToastDataPtr data) override; + void OnToastCancelled(ArcToastDataPtr data) override; // Methods called from ArcNotificationItem: void SendNotificationRemovedFromChrome(const std::string& key); void SendNotificationClickedOnChrome(const std::string& key); + void SendNotificationButtonClickedOnChrome( + const std::string& key, int button_index); private: - ArcBridgeService* const arc_bridge_; const AccountId main_profile_id_; + message_center::MessageCenter* const message_center_; using ItemMap = - base::ScopedPtrHashMap<std::string, scoped_ptr<ArcNotificationItem>>; + std::unordered_map<std::string, scoped_ptr<ArcNotificationItem>>; ItemMap items_; + bool ready_ = false; + mojo::Binding<arc::NotificationsHost> binding_; DISALLOW_COPY_AND_ASSIGN(ArcNotificationManager); diff --git a/chromium/ui/arc/notification/arc_notification_manager_unittest.cc b/chromium/ui/arc/notification/arc_notification_manager_unittest.cc new file mode 100644 index 00000000000..4241860725c --- /dev/null +++ b/chromium/ui/arc/notification/arc_notification_manager_unittest.cc @@ -0,0 +1,187 @@ +// 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 "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "components/arc/test/fake_arc_bridge_instance.h" +#include "components/arc/test/fake_arc_bridge_service.h" +#include "components/arc/test/fake_notifications_instance.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/arc/notification/arc_notification_manager.h" +#include "ui/message_center/fake_message_center.h" + +namespace arc { + +namespace { + +const char kDummyNotificationKey[] = "DUMMY_NOTIFICATION_KEY"; + +class MockMessageCenter : public message_center::FakeMessageCenter { + public: + MockMessageCenter() {} + ~MockMessageCenter() override { + STLDeleteContainerPointers( + visible_notifications_.begin(), visible_notifications_.end()); + } + + void AddNotification( + scoped_ptr<message_center::Notification> notification) override { + visible_notifications_.insert(notification.release()); + } + + void RemoveNotification(const std::string& id, bool by_user) override { + for (auto it = visible_notifications_.begin(); + it != visible_notifications_.end(); it++) { + if ((*it)->id() == id) { + delete *it; + visible_notifications_.erase(it); + return; + } + } + } + + const message_center::NotificationList::Notifications& + GetVisibleNotifications() override { + return visible_notifications_; + } + + private: + message_center::NotificationList::Notifications visible_notifications_; + + DISALLOW_COPY_AND_ASSIGN(MockMessageCenter); +}; + +class ArcBridgeServiceObserver : public ArcBridgeService::Observer { + public: + ArcBridgeServiceObserver() = default; + void OnNotificationsInstanceReady() override { ready_ = true; } + + bool IsReady() { return ready_; } + + private: + bool ready_ = false; + + DISALLOW_COPY_AND_ASSIGN(ArcBridgeServiceObserver); +}; + +} // anonymous namespace + +class ArcNotificationManagerTest : public testing::Test { + public: + ArcNotificationManagerTest() + : loop_(mojo::common::MessagePumpMojo::Create()) {} + ~ArcNotificationManagerTest() override { loop_.RunUntilIdle(); } + + protected: + FakeArcBridgeService* service() { return service_.get(); } + FakeNotificationsInstance* arc_notifications_instance() { + return arc_notifications_instance_.get(); + } + ArcNotificationManager* arc_notification_manager() { + return arc_notification_manager_.get(); + } + MockMessageCenter* message_center() { return message_center_.get(); } + + std::string CreateNotification() { + return CreateNotificationWithKey(kDummyNotificationKey); + } + + std::string CreateNotificationWithKey(const std::string& key) { + auto data = ArcNotificationData::New(); + data->key = key; + data->title = "TITLE"; + data->message = "MESSAGE"; + + std::vector<unsigned char> icon_data; + data->icon_data.Swap(&icon_data); + + arc_notification_manager()->OnNotificationPosted(std::move(data)); + + return key; + } + + private: + base::MessageLoop loop_; + scoped_ptr<FakeArcBridgeService> service_; + scoped_ptr<FakeNotificationsInstance> arc_notifications_instance_; + scoped_ptr<ArcNotificationManager> arc_notification_manager_; + scoped_ptr<MockMessageCenter> message_center_; + + void SetUp() override { + NotificationsInstancePtr arc_notifications_instance; + arc_notifications_instance_.reset( + new FakeNotificationsInstance(GetProxy(&arc_notifications_instance))); + service_.reset(new FakeArcBridgeService()); + message_center_.reset(new MockMessageCenter()); + + arc_notification_manager_.reset(new ArcNotificationManager( + service(), EmptyAccountId(), message_center_.get())); + + ArcBridgeServiceObserver observer; + service_->AddObserver(&observer); + service_->OnNotificationsInstanceReady( + std::move(arc_notifications_instance)); + + while (!observer.IsReady()) + loop_.RunUntilIdle(); + + service_->RemoveObserver(&observer); + } + + void TearDown() override { + arc_notification_manager_.reset(); + message_center_.reset(); + service_.reset(); + arc_notifications_instance_.reset(); + } + + DISALLOW_COPY_AND_ASSIGN(ArcNotificationManagerTest); +}; + +TEST_F(ArcNotificationManagerTest, NotificationCreatedAndRemoved) { + EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size()); + std::string key = CreateNotification(); + EXPECT_EQ(1u, message_center()->GetVisibleNotifications().size()); + + arc_notification_manager()->OnNotificationRemoved(key); + + EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size()); +} + +TEST_F(ArcNotificationManagerTest, NotificationRemovedByChrome) { + service()->SetReady(); + EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size()); + std::string key = CreateNotification(); + EXPECT_EQ(1u, message_center()->GetVisibleNotifications().size()); + + { + message_center::Notification* notification = + *message_center()->GetVisibleNotifications().begin(); + notification->delegate()->Close(true /* by_user */); + // |notification| gets stale here. + } + + arc_notifications_instance()->WaitForIncomingMethodCall(); + + ASSERT_EQ(1u, arc_notifications_instance()->events().size()); + EXPECT_EQ(key, arc_notifications_instance()->events().at(0).first); + EXPECT_EQ(ArcNotificationEvent::CLOSED, + arc_notifications_instance()->events().at(0).second); +} + +TEST_F(ArcNotificationManagerTest, NotificationRemovedByConnectionClose) { + service()->SetReady(); + EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size()); + CreateNotificationWithKey("notification1"); + CreateNotificationWithKey("notification2"); + CreateNotificationWithKey("notification3"); + EXPECT_EQ(3u, message_center()->GetVisibleNotifications().size()); + + arc_notification_manager()->OnNotificationsInstanceClosed(); + + EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size()); +} + +} // namespace arc diff --git a/chromium/ui/aura/BUILD.gn b/chromium/ui/aura/BUILD.gn index 6f112e67766..b1b32287118 100644 --- a/chromium/ui/aura/BUILD.gn +++ b/chromium/ui/aura/BUILD.gn @@ -15,8 +15,6 @@ component("aura") { "../wm/public/activation_delegate.h", "../wm/public/animation_host.cc", "../wm/public/animation_host.h", - "../wm/public/dispatcher_client.cc", - "../wm/public/dispatcher_client.h", "../wm/public/drag_drop_client.cc", "../wm/public/drag_drop_client.h", "../wm/public/drag_drop_delegate.cc", @@ -68,8 +66,6 @@ component("aura") { "layout_manager.h", "mus/mus_util.cc", "mus/mus_util.h", - "remote_window_tree_host_win.cc", - "remote_window_tree_host_win.h", "scoped_window_targeter.cc", "scoped_window_targeter.h", "window.cc", @@ -135,7 +131,6 @@ component("aura") { deps += [ "//ipc", - "//ui/metro_viewer", "//ui/platform_window/win", ] } @@ -252,46 +247,6 @@ executable("demo") { } } -executable("bench") { - output_name = "aura_bench" - testonly = true - - sources = [ - "bench/bench_main.cc", - ] - - deps = [ - ":test_support", - "//base", - "//base:i18n", - "//build/config/sanitizers:deps", - "//cc", - "//gpu/command_buffer/client:gles2_interface", - "//skia", - "//third_party/icu", - "//ui/base", - "//ui/compositor", - "//ui/compositor:test_support", - "//ui/events", - "//ui/gfx", - "//ui/gfx/geometry", - "//ui/gl", - ] - - if (use_x11) { - deps += [ "//ui/gfx/x" ] - } -} - -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("aura_unittests_run") { - testonly = true - deps = [ - ":aura_unittests", - ] -} - test("aura_unittests") { sources = [ "gestures/gesture_recognizer_unittest.cc", @@ -303,7 +258,6 @@ test("aura_unittests") { deps = [ ":test_support", - "//base/allocator", "//base/test:test_support", "//skia", "//testing/gtest", diff --git a/chromium/ui/aura/aura.gyp b/chromium/ui/aura/aura.gyp index 12230e22965..3020e2d4da2 100644 --- a/chromium/ui/aura/aura.gyp +++ b/chromium/ui/aura/aura.gyp @@ -37,8 +37,6 @@ '../wm/public/activation_delegate.h', '../wm/public/animation_host.cc', '../wm/public/animation_host.h', - '../wm/public/dispatcher_client.cc', - '../wm/public/dispatcher_client.h', '../wm/public/drag_drop_client.cc', '../wm/public/drag_drop_client.h', '../wm/public/drag_drop_delegate.cc', @@ -88,8 +86,8 @@ 'input_state_lookup_win.h', 'layout_manager.cc', 'layout_manager.h', - 'remote_window_tree_host_win.cc', - 'remote_window_tree_host_win.h', + 'mus/mus_util.cc', + 'mus/mus_util.h', 'scoped_window_targeter.cc', 'scoped_window_targeter.h', 'window.cc', @@ -126,7 +124,6 @@ }], ['OS=="win"', { 'dependencies': [ - '../metro_viewer/metro_viewer.gyp:metro_viewer_messages', '../platform_window/win/win_window.gyp:win_window', '../../ipc/ipc.gyp:ipc', ], @@ -140,12 +137,6 @@ '../ozone/ozone.gyp:ozone_base', ], }], - ['OS!="win" and OS!="android" and use_ozone==0', { - 'sources!': [ - 'window_tree_host_platform.cc', - 'window_tree_host_platform.h', - ], - }], ['OS=="android"', { 'dependencies': [ '../platform_window/android/android_window.gyp:android_window', @@ -243,40 +234,6 @@ ] }, { - 'target_name': 'aura_bench', - 'type': 'executable', - 'dependencies': [ - '../../base/base.gyp:base', - '../../base/base.gyp:base_i18n', - '../../cc/cc.gyp:cc', - '../../gpu/gpu.gyp:gles2_implementation', - '../../skia/skia.gyp:skia', - '../../third_party/icu/icu.gyp:icui18n', - '../../third_party/icu/icu.gyp:icuuc', - '../base/ui_base.gyp:ui_base', - '../compositor/compositor.gyp:compositor', - '../compositor/compositor.gyp:compositor_test_support', - '../events/events.gyp:events', - '../gfx/gfx.gyp:gfx', - '../gfx/gfx.gyp:gfx_geometry', - 'aura', - 'aura_test_support', - ], - 'include_dirs': [ - '..', - ], - 'sources': [ - 'bench/bench_main.cc', - ], - 'conditions': [ - ['use_x11==1', { - 'dependencies': [ - '../gfx/x/gfx_x11.gyp:gfx_x11', - ], - }], - ] - }, - { 'target_name': 'aura_unittests', 'type': 'executable', 'dependencies': [ @@ -314,12 +271,6 @@ '<(DEPTH)/third_party/mesa/mesa.gyp:osmesa', ], }], - ['OS=="linux" and use_allocator!="none"', { - 'dependencies': [ - # See http://crbug.com/162998#c4 for why this is needed. - '../../base/allocator/allocator.gyp:allocator', - ], - }], ], }, ], diff --git a/chromium/ui/aura/bench/DEPS b/chromium/ui/aura/bench/DEPS deleted file mode 100644 index 315ad72c16e..00000000000 --- a/chromium/ui/aura/bench/DEPS +++ /dev/null @@ -1,8 +0,0 @@ -include_rules = [ - "+cc", - "-cc/blink", - "-cc/surfaces", - "+gpu/command_buffer/client/gles2_interface.h", - "+third_party/khronos", - "+ui/gl/gl_surface.h", # To initialize GL bindings. -] diff --git a/chromium/ui/aura/bench/bench_main.cc b/chromium/ui/aura/bench/bench_main.cc deleted file mode 100644 index 918990a3679..00000000000 --- a/chromium/ui/aura/bench/bench_main.cc +++ /dev/null @@ -1,389 +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. - -#if defined(USE_X11) -#include <X11/Xlib.h> -#endif - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/i18n/icu_util.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string_split.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "cc/output/context_provider.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/skia/include/core/SkXfermode.h" -#include "ui/aura/client/default_capture_client.h" -#include "ui/aura/env.h" -#include "ui/aura/test/test_focus_client.h" -#include "ui/aura/test/test_screen.h" -#include "ui/aura/window.h" -#include "ui/aura/window_tree_host.h" -#include "ui/base/hit_test.h" -#include "ui/compositor/compositor.h" -#include "ui/compositor/compositor_observer.h" -#include "ui/compositor/debug_utils.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/paint_recorder.h" -#include "ui/compositor/test/in_process_context_factory.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/skia_util.h" -#include "ui/gfx/x/x11_connection.h" -#include "ui/gl/gl_surface.h" - -#ifndef GL_GLEXT_PROTOTYPES -#define GL_GLEXT_PROTOTYPES 1 -#endif -#include "third_party/khronos/GLES2/gl2ext.h" - -using base::TimeTicks; -using ui::Compositor; -using ui::Layer; -using ui::LayerDelegate; - -namespace { - -class ColoredLayer : public Layer, public LayerDelegate { - public: - explicit ColoredLayer(SkColor color) - : Layer(ui::LAYER_TEXTURED), - color_(color), - draw_(true) { - set_delegate(this); - } - - ~ColoredLayer() override {} - - // Overridden from LayerDelegate: - void OnPaintLayer(const ui::PaintContext& context) override { - if (draw_) { - ui::PaintRecorder recorder(context, size()); - recorder.canvas()->DrawColor(color_); - } - } - - void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {} - - void OnDeviceScaleFactorChanged(float device_scale_factor) override {} - - base::Closure PrepareForLayerBoundsChange() override { - return base::Closure(); - } - - void set_color(SkColor color) { color_ = color; } - void set_draw(bool draw) { draw_ = draw; } - - private: - SkColor color_; - bool draw_; - - DISALLOW_COPY_AND_ASSIGN(ColoredLayer); -}; - -const int kFrames = 100; - -// Benchmark base class, hooks up drawing callback and displaying FPS. -class BenchCompositorObserver : public ui::CompositorObserver { - public: - explicit BenchCompositorObserver(int max_frames) - : start_time_(), - frames_(0), - max_frames_(max_frames) { - } - virtual ~BenchCompositorObserver() {} - - void OnCompositingDidCommit(ui::Compositor* compositor) override {} - - void OnCompositingStarted(Compositor* compositor, - base::TimeTicks start_time) override {} - - void OnCompositingEnded(Compositor* compositor) override { - if (start_time_.is_null()) { - start_time_ = TimeTicks::Now(); - } else { - ++frames_; - if (frames_ % kFrames == 0) { - TimeTicks now = TimeTicks::Now(); - double ms = (now - start_time_).InMillisecondsF() / kFrames; - LOG(INFO) << "FPS: " << 1000.f / ms << " (" << ms << " ms)"; - start_time_ = now; - } - } - if (max_frames_ && frames_ == max_frames_) { - base::MessageLoop::current()->QuitWhenIdle(); - } else { - Draw(); - } - } - - void OnCompositingAborted(Compositor* compositor) override {} - - void OnCompositingLockStateChanged(Compositor* compositor) override {} - - void OnCompositingShuttingDown(ui::Compositor* compositor) override {} - - virtual void Draw() {} - - int frames() const { return frames_; } - - private: - TimeTicks start_time_; - int frames_; - int max_frames_; - - DISALLOW_COPY_AND_ASSIGN(BenchCompositorObserver); -}; - -void ReturnMailbox(scoped_refptr<cc::ContextProvider> context_provider, - GLuint texture, - const gpu::SyncToken& sync_token, - bool is_lost) { - gpu::gles2::GLES2Interface* gl = context_provider->ContextGL(); - gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); - gl->DeleteTextures(1, &texture); - gl->ShallowFlushCHROMIUM(); -} - -gfx::Size GetFullscreenSize() { -#if defined(OS_WIN) - return gfx::Size(GetSystemMetrics(SM_CXSCREEN), - GetSystemMetrics(SM_CYSCREEN)); -#else - NOTIMPLEMENTED(); - return gfx::Size(1024, 768); -#endif -} - -// A benchmark that adds a texture layer that is updated every frame. -class WebGLBench : public BenchCompositorObserver { - public: - WebGLBench(ui::ContextFactory* context_factory, - Layer* parent, - Compositor* compositor, - int max_frames) - : BenchCompositorObserver(max_frames), - parent_(parent), - webgl_(ui::LAYER_SOLID_COLOR), - compositor_(compositor), - fbo_(0), - do_draw_(true) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - do_draw_ = !command_line->HasSwitch("disable-draw"); - - std::string webgl_size = command_line->GetSwitchValueASCII("webgl-size"); - int width = 0; - int height = 0; - if (!webgl_size.empty()) { - std::vector<std::string> split_size = base::SplitString( - webgl_size, "x", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - if (split_size.size() == 2) { - width = atoi(split_size[0].c_str()); - height = atoi(split_size[1].c_str()); - } - } - if (!width || !height) { - width = 800; - height = 600; - } - gfx::Rect bounds(width, height); - webgl_.SetBounds(bounds); - parent_->Add(&webgl_); - - context_provider_ = context_factory->SharedMainThreadContextProvider(); - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - GLuint texture = 0; - gl->GenTextures(1, &texture); - gl->BindTexture(GL_TEXTURE_2D, texture); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - gl->TexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - width, - height, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - NULL); - gpu::Mailbox mailbox; - gl->GenMailboxCHROMIUM(mailbox.name); - gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); - - gl->GenFramebuffers(1, &fbo_); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - gl->FramebufferTexture2D( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); - gl->ClearColor(0.f, 1.f, 0.f, 1.f); - gl->Clear(GL_COLOR_BUFFER_BIT); - - const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM(); - gl->Flush(); - - gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); - webgl_.SetTextureMailbox( - cc::TextureMailbox(mailbox, sync_token, GL_TEXTURE_2D), - cc::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, context_provider_, texture)), - bounds.size()); - compositor->AddObserver(this); - } - - ~WebGLBench() override { - webgl_.SetShowSolidColorContent(); - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->DeleteFramebuffers(1, &fbo_); - compositor_->RemoveObserver(this); - } - - void Draw() override { - if (do_draw_) { - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - gl->ClearColor((frames() % kFrames)*1.0/kFrames, 1.f, 0.f, 1.f); - gl->Clear(GL_COLOR_BUFFER_BIT); - gl->Flush(); - } - webgl_.SchedulePaint(gfx::Rect(webgl_.bounds().size())); - compositor_->ScheduleDraw(); - } - - private: - Layer* parent_; - Layer webgl_; - Compositor* compositor_; - scoped_refptr<cc::ContextProvider> context_provider_; - - // The FBO that is used to render to the texture. - unsigned int fbo_; - - // Whether or not to draw to the texture every frame. - bool do_draw_; - - DISALLOW_COPY_AND_ASSIGN(WebGLBench); -}; - -// A benchmark that paints (in software) all tiles every frame. -class SoftwareScrollBench : public BenchCompositorObserver { - public: - SoftwareScrollBench(ColoredLayer* layer, - Compositor* compositor, - int max_frames) - : BenchCompositorObserver(max_frames), - layer_(layer), - compositor_(compositor) { - compositor->AddObserver(this); - layer_->set_draw( - !base::CommandLine::ForCurrentProcess()->HasSwitch("disable-draw")); - } - - ~SoftwareScrollBench() override { compositor_->RemoveObserver(this); } - - void Draw() override { - layer_->set_color( - SkColorSetARGBInline(255*(frames() % kFrames)/kFrames, 255, 0, 255)); - layer_->SchedulePaint(gfx::Rect(layer_->bounds().size())); - } - - private: - ColoredLayer* layer_; - Compositor* compositor_; - - DISALLOW_COPY_AND_ASSIGN(SoftwareScrollBench); -}; - -} // namespace - -int main(int argc, char** argv) { - base::CommandLine::Init(argc, argv); - - base::AtExitManager exit_manager; - -#if defined(USE_X11) - // This demo uses InProcessContextFactory which uses X on a separate Gpu - // thread. - gfx::InitializeThreadedX11(); -#endif - - gfx::GLSurface::InitializeOneOff(); - - // The ContextFactory must exist before any Compositors are created. - bool context_factory_for_test = false; - scoped_ptr<ui::InProcessContextFactory> context_factory( - new ui::InProcessContextFactory(context_factory_for_test, nullptr)); - - base::i18n::InitializeICU(); - - base::MessageLoopForUI message_loop; - aura::Env::CreateInstance(true); - aura::Env::GetInstance()->set_context_factory(context_factory.get()); - scoped_ptr<aura::TestScreen> test_screen( - aura::TestScreen::Create(GetFullscreenSize())); - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen.get()); - scoped_ptr<aura::WindowTreeHost> host( - test_screen->CreateHostForPrimaryDisplay()); - aura::client::SetCaptureClient( - host->window(), - new aura::client::DefaultCaptureClient(host->window())); - - scoped_ptr<aura::client::FocusClient> focus_client( - new aura::test::TestFocusClient); - aura::client::SetFocusClient(host->window(), focus_client.get()); - - // add layers - ColoredLayer background(SK_ColorRED); - background.SetBounds(host->window()->bounds()); - host->window()->layer()->Add(&background); - - ColoredLayer window(SK_ColorBLUE); - window.SetBounds(gfx::Rect(background.bounds().size())); - background.Add(&window); - - Layer content_layer(ui::LAYER_NOT_DRAWN); - - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - bool force = command_line->HasSwitch("force-render-surface"); - content_layer.SetForceRenderSurface(force); - gfx::Rect bounds(window.bounds().size()); - bounds.Inset(0, 30, 0, 0); - content_layer.SetBounds(bounds); - window.Add(&content_layer); - - ColoredLayer page_background(SK_ColorWHITE); - page_background.SetBounds(gfx::Rect(content_layer.bounds().size())); - content_layer.Add(&page_background); - - int frames = atoi(command_line->GetSwitchValueASCII("frames").c_str()); - scoped_ptr<BenchCompositorObserver> bench; - - if (command_line->HasSwitch("bench-software-scroll")) { - bench.reset(new SoftwareScrollBench(&page_background, - host->compositor(), - frames)); - } else { - bench.reset(new WebGLBench(context_factory.get(), - &page_background, - host->compositor(), - frames)); - } - -#ifndef NDEBUG - ui::PrintLayerHierarchy(host->window()->layer(), gfx::Point(100, 100)); -#endif - - host->Show(); - base::MessageLoopForUI::current()->Run(); - focus_client.reset(); - host.reset(); - - return 0; -} diff --git a/chromium/ui/aura/demo/demo_main.cc b/chromium/ui/aura/demo/demo_main.cc index f485a8ce954..ea86753f8b3 100644 --- a/chromium/ui/aura/demo/demo_main.cc +++ b/chromium/ui/aura/demo/demo_main.cc @@ -30,7 +30,7 @@ #include "ui/gl/gl_surface.h" #if defined(USE_X11) -#include "ui/gfx/x/x11_connection.h" +#include "ui/gfx/x/x11_connection.h" // nogncheck #endif #if defined(OS_WIN) @@ -150,7 +150,7 @@ int DemoMain() { aura::Env::GetInstance()->set_context_factory(context_factory.get()); scoped_ptr<aura::TestScreen> test_screen( aura::TestScreen::Create(gfx::Size())); - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen.get()); + gfx::Screen::SetScreenInstance(test_screen.get()); scoped_ptr<aura::WindowTreeHost> host( test_screen->CreateHostForPrimaryDisplay()); scoped_ptr<DemoWindowTreeClient> window_tree_client( diff --git a/chromium/ui/aura/env.cc b/chromium/ui/aura/env.cc index 148300ca43b..7e82d4e13cd 100644 --- a/chromium/ui/aura/env.cc +++ b/chromium/ui/aura/env.cc @@ -4,6 +4,7 @@ #include "ui/aura/env.h" +#include "base/command_line.h" #include "base/lazy_instance.h" #include "base/threading/thread_local.h" #include "ui/aura/env_observer.h" @@ -23,6 +24,14 @@ namespace { base::LazyInstance<base::ThreadLocalPointer<Env> >::Leaky lazy_tls_ptr = LAZY_INSTANCE_INITIALIZER; +#if defined(USE_OZONE) +// Returns true if running inside of mus. Checks for mojo specific flag. +bool RunningInsideMus() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + "primordial-pipe-token"); +} +#endif + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -84,12 +93,16 @@ Env::~Env() { } void Env::Init(bool create_event_source) { + if (!create_event_source) + return; #if defined(USE_OZONE) // The ozone platform can provide its own event source. So initialize the - // platform before creating the default event source. - ui::OzonePlatform::InitializeForUI(); + // platform before creating the default event source. If running inside mus + // let the mus process initialize ozone instead. + if (!RunningInsideMus()) + ui::OzonePlatform::InitializeForUI(); #endif - if (create_event_source && !ui::PlatformEventSource::GetInstance()) + if (!ui::PlatformEventSource::GetInstance()) event_source_ = ui::PlatformEventSource::CreateDefault(); } diff --git a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc index 820c914672d..64057fe5814 100644 --- a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc +++ b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc @@ -638,6 +638,18 @@ void DelayByShowPressTimeout() { run_loop.Run(); } +void SetTouchRadius(ui::TouchEvent* event, float radius_x, float radius_y) { + // Using ctor (over direct struct access) due to it's special behavior with + // radii. + ui::PointerDetails details(ui::EventPointerType::POINTER_TYPE_TOUCH, + radius_x, + radius_y, + event->pointer_details().force, + event->pointer_details().tilt_x, + event->pointer_details().tilt_y); + event->set_pointer_details(details); +} + } // namespace class GestureRecognizerTest : public AuraTestBase, @@ -738,8 +750,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); - press.set_radius_x(5); - press.set_radius_y(12); + SetTouchRadius(&press, 5, 12); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); @@ -755,8 +766,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); - release.set_radius_x(5); - release.set_radius_y(12); + SetTouchRadius(&release, 5, 12); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); @@ -781,8 +791,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(365, 290), kTouchId, tes.Now()); - press.set_radius_x(8); - press.set_radius_y(14); + SetTouchRadius(&press, 8, 14); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); @@ -796,8 +805,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(367, 291), kTouchId, tes.LeapForward(50)); - release.set_radius_x(20); - release.set_radius_y(13); + SetTouchRadius(&release, 20, 13); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); @@ -822,8 +830,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(46, 205), kTouchId, tes.Now()); - press.set_radius_x(6); - press.set_radius_y(10); + SetTouchRadius(&press, 6, 10); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); @@ -838,8 +845,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(49, 204), kTouchId, tes.LeapForward(50)); - move.set_radius_x(8); - move.set_radius_y(12); + SetTouchRadius(&move, 8, 12); DispatchEventUsingWindowDispatcher(&move); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); @@ -853,8 +859,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(49, 204), kTouchId, tes.LeapForward(50)); - release.set_radius_x(4); - release.set_radius_y(8); + SetTouchRadius(&release, 4, 8); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); @@ -879,8 +884,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(400, 150), kTouchId, tes.Now()); - press.set_radius_x(7); - press.set_radius_y(10); + SetTouchRadius(&press, 7, 10); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); @@ -894,8 +898,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(397, 151), kTouchId, tes.LeapForward(50)); - move.set_radius_x(13); - move.set_radius_y(12); + SetTouchRadius(&move, 13, 12); DispatchEventUsingWindowDispatcher(&move); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); @@ -909,8 +912,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(397, 149), kTouchId, tes.LeapForward(50)); - move1.set_radius_x(16); - move1.set_radius_y(16); + SetTouchRadius(&move1, 16, 16); DispatchEventUsingWindowDispatcher(&move1); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); @@ -924,8 +926,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(400, 150), kTouchId, tes.LeapForward(50)); - move2.set_radius_x(14); - move2.set_radius_y(10); + SetTouchRadius(&move2, 14, 10); DispatchEventUsingWindowDispatcher(&move2); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); @@ -939,8 +940,7 @@ TEST_F(GestureRecognizerTest, GestureEventTapRegion) { delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(401, 149), kTouchId, tes.LeapForward(50)); - release.set_radius_x(8); - release.set_radius_y(9); + SetTouchRadius(&release, 8, 9); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); @@ -2532,7 +2532,7 @@ TEST_F(GestureRecognizerTest, PressDoesNotCrash) { delegate.get(), -1234, gfx::Rect(10, 10, 300, 300), root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(45, 45), 7, tes.Now()); - press.set_radius_x(40); + SetTouchRadius(&press, 40, 0); DispatchEventUsingWindowDispatcher(&press); EXPECT_TRUE(delegate->tap_down()); EXPECT_EQ(gfx::Rect(5, 5, 80, 80).ToString(), @@ -3462,7 +3462,7 @@ TEST_F(GestureRecognizerTest, BoundingBoxRadiusChange) { ui::TouchEvent press2( ui::ET_TOUCH_PRESSED, gfx::Point(201, 201), kTouchId2, tes.LeapForward(400)); - press2.set_radius_x(5); + SetTouchRadius(&press2, 5, 0); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(delegate->pinch_begin()); EXPECT_EQ(gfx::Rect(101, 196, 105, 10).ToString(), @@ -3482,8 +3482,7 @@ TEST_F(GestureRecognizerTest, BoundingBoxRadiusChange) { // The position doesn't move, but the radius changes. ui::TouchEvent move2( ui::ET_TOUCH_MOVED, gfx::Point(50, 50), kTouchId, tes.LeapForward(40)); - move2.set_radius_x(50); - move2.set_radius_y(60); + SetTouchRadius(&move2, 50, 60); DispatchEventUsingWindowDispatcher(&move2); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_cancel()); diff --git a/chromium/ui/aura/mus/mus_util.cc b/chromium/ui/aura/mus/mus_util.cc index ed57f6a6e1a..468b336c450 100644 --- a/chromium/ui/aura/mus/mus_util.cc +++ b/chromium/ui/aura/mus/mus_util.cc @@ -5,17 +5,26 @@ #include "ui/aura/mus/mus_util.h" #include "ui/aura/window.h" +#include "ui/aura/window_property.h" namespace aura { +namespace { + +// This code uses Set/GetNativeWindowProperty instead of Set/GetProperty to +// avoid a dependency on mus. +const char kMusWindowKey[] = "mus"; + +} // namespace mus::Window* GetMusWindow(Window* window) { if (!window) return nullptr; - return static_cast<mus::Window*>(window->GetNativeWindowProperty("mus")); + return static_cast<mus::Window*>( + window->GetNativeWindowProperty(kMusWindowKey)); } void SetMusWindow(Window* window, mus::Window* mus_window) { - window->SetNativeWindowProperty("mus", mus_window); + window->SetNativeWindowProperty(kMusWindowKey, mus_window); } } // namespace aura diff --git a/chromium/ui/aura/remote_window_tree_host_win.cc b/chromium/ui/aura/remote_window_tree_host_win.cc deleted file mode 100644 index a303d98ec99..00000000000 --- a/chromium/ui/aura/remote_window_tree_host_win.cc +++ /dev/null @@ -1,522 +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/aura/remote_window_tree_host_win.h" - -#include <windows.h> -#include <stddef.h> - -#include <algorithm> - -#include "base/message_loop/message_loop.h" -#include "ipc/ipc_message.h" -#include "ipc/ipc_sender.h" -#include "ui/aura/client/cursor_client.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/aura/window_property.h" -#include "ui/base/cursor/cursor_loader_win.h" -#include "ui/base/ime/composition_text.h" -#include "ui/base/ime/input_method.h" -#include "ui/base/ime/remote_input_method_win.h" -#include "ui/base/ime/text_input_client.h" -#include "ui/base/view_prop.h" -#include "ui/events/event_utils.h" -#include "ui/events/keycodes/keyboard_code_conversion_win.h" -#include "ui/gfx/geometry/insets.h" -#include "ui/gfx/win/dpi.h" -#include "ui/metro_viewer/metro_viewer_messages.h" - -namespace aura { - -namespace { - -const char* kWindowTreeHostWinKey = "__AURA_REMOTE_WINDOW_TREE_HOST_WIN__"; - -// Sets the keystate for the virtual key passed in to down or up. -void SetKeyState(uint8_t* key_states, - bool key_down, - uint32_t virtual_key_code) { - DCHECK(key_states); - - if (key_down) - key_states[virtual_key_code] |= 0x80; - else - key_states[virtual_key_code] &= 0x7F; -} - -// Sets the keyboard states for the Shift/Control/Alt/Caps lock keys. -void SetVirtualKeyStates(uint32_t flags) { - uint8_t keyboard_state[256] = {0}; - ::GetKeyboardState(keyboard_state); - - SetKeyState(keyboard_state, !!(flags & ui::EF_SHIFT_DOWN), VK_SHIFT); - SetKeyState(keyboard_state, !!(flags & ui::EF_CONTROL_DOWN), VK_CONTROL); - SetKeyState(keyboard_state, !!(flags & ui::EF_ALT_DOWN), VK_MENU); - SetKeyState(keyboard_state, !!(flags & ui::EF_CAPS_LOCK_ON), VK_CAPITAL); - SetKeyState(keyboard_state, !!(flags & ui::EF_LEFT_MOUSE_BUTTON), VK_LBUTTON); - SetKeyState(keyboard_state, !!(flags & ui::EF_RIGHT_MOUSE_BUTTON), - VK_RBUTTON); - SetKeyState(keyboard_state, !!(flags & ui::EF_MIDDLE_MOUSE_BUTTON), - VK_MBUTTON); - - ::SetKeyboardState(keyboard_state); -} - -void FillCompositionText( - const base::string16& text, - int32_t selection_start, - int32_t selection_end, - const std::vector<metro_viewer::UnderlineInfo>& underlines, - ui::CompositionText* composition_text) { - composition_text->Clear(); - composition_text->text = text; - composition_text->selection.set_start(selection_start); - composition_text->selection.set_end(selection_end); - composition_text->underlines.resize(underlines.size()); - for (size_t i = 0; i < underlines.size(); ++i) { - composition_text->underlines[i].start_offset = underlines[i].start_offset; - composition_text->underlines[i].end_offset = underlines[i].end_offset; - composition_text->underlines[i].color = SK_ColorBLACK; - composition_text->underlines[i].thick = underlines[i].thick; - composition_text->underlines[i].background_color = SK_ColorTRANSPARENT; - } -} - -} // namespace - -RemoteWindowTreeHostWin* g_instance = NULL; - -// static -RemoteWindowTreeHostWin* RemoteWindowTreeHostWin::Instance() { - return g_instance; -} - -RemoteWindowTreeHostWin::RemoteWindowTreeHostWin() - : remote_window_(NULL), - host_(NULL), - ignore_mouse_moves_until_set_cursor_ack_(0), - event_flags_(0), - window_size_(GetSystemMetrics(SM_CXSCREEN), - GetSystemMetrics(SM_CYSCREEN)) { - CHECK(!g_instance); - g_instance = this; - prop_.reset(new ui::ViewProp(NULL, kWindowTreeHostWinKey, this)); - CreateCompositor(); - OnAcceleratedWidgetAvailable(); -} - -RemoteWindowTreeHostWin::~RemoteWindowTreeHostWin() { - DestroyCompositor(); - DestroyDispatcher(); - DCHECK_EQ(g_instance, this); - g_instance = NULL; -} - -// static -bool RemoteWindowTreeHostWin::IsValid() { - return Instance()->remote_window_ != NULL; -} - -void RemoteWindowTreeHostWin::SetRemoteWindowHandle(HWND remote_window) { - remote_window_ = remote_window; -} - -void RemoteWindowTreeHostWin::Connected(IPC::Sender* host) { - CHECK(host_ == NULL); - DCHECK(remote_window_); - host_ = host; - // Recreate the compositor for the target surface represented by the - // remote_window HWND. - CreateCompositor(); - OnAcceleratedWidgetAvailable(); - InitCompositor(); -} - -void RemoteWindowTreeHostWin::Disconnected() { - // Don't CHECK here, Disconnected is called on a channel error which can - // happen before we're successfully Connected. - if (!host_) - return; - ui::RemoteInputMethodPrivateWin* remote_input_method_private = - GetRemoteInputMethodPrivate(); - if (remote_input_method_private) - remote_input_method_private->SetRemoteDelegate(NULL); - host_ = NULL; - remote_window_ = NULL; -} - -bool RemoteWindowTreeHostWin::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(RemoteWindowTreeHostWin, message) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseMoved, OnMouseMoved) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MouseButton, OnMouseButton) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyDown, OnKeyDown) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_KeyUp, OnKeyUp) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_Character, OnChar) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowActivated, - OnWindowActivated) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_EdgeGesture, OnEdgeGesture) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchDown, - OnTouchDown) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchUp, - OnTouchUp) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_TouchMoved, - OnTouchMoved) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPosAck, - OnSetCursorPosAck) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCandidatePopupChanged, - OnImeCandidatePopupChanged) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCompositionChanged, - OnImeCompositionChanged) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeTextCommitted, - OnImeTextCommitted) - IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeInputSourceChanged, - OnImeInputSourceChanged) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void RemoteWindowTreeHostWin::HandleOpenURLOnDesktop( - const base::FilePath& shortcut, - const base::string16& url) { - if (!host_) - return; - host_->Send(new MetroViewerHostMsg_OpenURLOnDesktop(shortcut, url)); -} - -void RemoteWindowTreeHostWin::HandleWindowSizeChanged(uint32_t width, - uint32_t height) { - SetBounds(gfx::Rect(0, 0, width, height)); -} - -bool RemoteWindowTreeHostWin::IsForegroundWindow() { - return ::GetForegroundWindow() == remote_window_; -} - -Window* RemoteWindowTreeHostWin::GetAshWindow() { - return window(); -} - -ui::EventSource* RemoteWindowTreeHostWin::GetEventSource() { - return this; -} - -gfx::AcceleratedWidget RemoteWindowTreeHostWin::GetAcceleratedWidget() { - if (remote_window_) - return remote_window_; - // Getting here should only happen for ash_unittests.exe and related code. - return ::GetDesktopWindow(); -} - -void RemoteWindowTreeHostWin::ShowImpl() { - ui::RemoteInputMethodPrivateWin* remote_input_method_private = - GetRemoteInputMethodPrivate(); - if (remote_input_method_private) - remote_input_method_private->SetRemoteDelegate(this); -} - -void RemoteWindowTreeHostWin::HideImpl() { - NOTIMPLEMENTED(); -} - -gfx::Rect RemoteWindowTreeHostWin::GetBounds() const { - return gfx::Rect(window_size_); -} - -void RemoteWindowTreeHostWin::SetBounds(const gfx::Rect& bounds) { - window_size_ = bounds.size(); - OnHostResized(bounds.size()); -} - -gfx::Point RemoteWindowTreeHostWin::GetLocationOnNativeScreen() const { - return gfx::Point(0, 0); -} - -void RemoteWindowTreeHostWin::SetCapture() { -} - -void RemoteWindowTreeHostWin::ReleaseCapture() { -} - -void RemoteWindowTreeHostWin::SetCursorNative(gfx::NativeCursor native_cursor) { - if (!host_) - return; - host_->Send( - new MetroViewerHostMsg_SetCursor(uint64_t(native_cursor.platform()))); -} - -void RemoteWindowTreeHostWin::MoveCursorToNative(const gfx::Point& location) { - VLOG(1) << "In MoveCursorTo: " << location.x() << ", " << location.y(); - if (!host_) - return; - - // This function can be called in cases like when the mouse cursor is - // restricted within a viewport (For e.g. LockCursor) which assumes that - // subsequent mouse moves would be received starting with the new cursor - // coordinates. This is a challenge for Windows ASH for the reasons - // outlined below. - // Other cases which don't expect this behavior should continue to work - // without issues. - - // The mouse events are received by the viewer process and sent to the - // browser. If we invoke the SetCursor API here we continue to receive - // mouse messages from the viewer which were posted before the SetCursor - // API executes which messes up the state in the browser. To workaround - // this we invoke the SetCursor API in the viewer process and ignore - // mouse messages until we received an ACK from the viewer indicating that - // the SetCursor operation completed. - ignore_mouse_moves_until_set_cursor_ack_++; - VLOG(1) << "In MoveCursorTo. Sending IPC"; - host_->Send(new MetroViewerHostMsg_SetCursorPos(location.x(), location.y())); -} - -void RemoteWindowTreeHostWin::OnCursorVisibilityChangedNative(bool show) { - NOTIMPLEMENTED(); -} - -void RemoteWindowTreeHostWin::CancelComposition() { - if (!host_) - return; - host_->Send(new MetroViewerHostMsg_ImeCancelComposition); -} - -void RemoteWindowTreeHostWin::OnTextInputClientUpdated( - const std::vector<int32_t>& input_scopes, - const std::vector<gfx::Rect>& composition_character_bounds) { - if (!host_) - return; - std::vector<metro_viewer::CharacterBounds> character_bounds; - for (size_t i = 0; i < composition_character_bounds.size(); ++i) { - const gfx::Rect& rect = composition_character_bounds[i]; - metro_viewer::CharacterBounds bounds; - bounds.left = rect.x(); - bounds.top = rect.y(); - bounds.right = rect.right(); - bounds.bottom = rect.bottom(); - character_bounds.push_back(bounds); - } - host_->Send(new MetroViewerHostMsg_ImeTextInputClientUpdated( - input_scopes, character_bounds)); -} - -gfx::Point PointFromNativeEvent(int32_t x, int32_t y) { - static float scale_factor = gfx::GetDPIScale(); - gfx::Point result( x * scale_factor, y * scale_factor); - return result; -} - -void RemoteWindowTreeHostWin::OnMouseMoved(int32_t x, - int32_t y, - int32_t flags) { - if (ignore_mouse_moves_until_set_cursor_ack_) - return; - - gfx::Point location = PointFromNativeEvent(x, y); - ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location, - ui::EventTimeForNow(), flags, 0); - SendEventToProcessor(&event); -} - -void RemoteWindowTreeHostWin::OnMouseButton( - const MetroViewerHostMsg_MouseButtonParams& params) { - gfx::Point location = PointFromNativeEvent(params.x, params.y); - ui::MouseEvent mouse_event( - params.event_type, location, location, ui::EventTimeForNow(), - static_cast<int>(params.flags), static_cast<int>(params.changed_button)); - - SetEventFlags(params.flags | key_event_flags()); - if (params.event_type == ui::ET_MOUSEWHEEL) { - int x_offset = params.is_horizontal_wheel ? params.extra : 0; - int y_offset = !params.is_horizontal_wheel ? params.extra : 0; - ui::MouseWheelEvent wheel_event(mouse_event, x_offset, y_offset); - SendEventToProcessor(&wheel_event); - } else if (params.event_type == ui::ET_MOUSE_PRESSED) { - // TODO(shrikant): Ideally modify code in event.cc by adding automatic - // tracking of double clicks in synthetic MouseEvent constructor code. - // Non-synthetic MouseEvent constructor code does automatically track - // this. Need to use some caution while modifying synthetic constructor - // as many tests and other code paths depend on it and apparently - // specifically depend on non implicit tracking of previous mouse event. - if (last_mouse_click_event_ && - ui::MouseEvent::IsRepeatedClickEvent(mouse_event, - *last_mouse_click_event_)) { - mouse_event.SetClickCount(2); - } else { - mouse_event.SetClickCount(1); - } - last_mouse_click_event_ .reset(new ui::MouseEvent(mouse_event)); - SendEventToProcessor(&mouse_event); - } else { - SendEventToProcessor(&mouse_event); - } -} - -void RemoteWindowTreeHostWin::OnKeyDown(uint32_t vkey, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags) { - DispatchKeyboardMessage(ui::ET_KEY_PRESSED, vkey, repeat_count, scan_code, - flags, false); -} - -void RemoteWindowTreeHostWin::OnKeyUp(uint32_t vkey, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags) { - DispatchKeyboardMessage(ui::ET_KEY_RELEASED, vkey, repeat_count, scan_code, - flags, false); -} - -void RemoteWindowTreeHostWin::OnChar(uint32_t key_code, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags) { - DispatchKeyboardMessage(ui::ET_KEY_PRESSED, key_code, repeat_count, - scan_code, flags, true); -} - -void RemoteWindowTreeHostWin::OnWindowActivated(bool repaint) { - OnHostActivated(); - if (repaint && compositor()) - compositor()->ScheduleFullRedraw(); -} - -void RemoteWindowTreeHostWin::OnEdgeGesture() { - ui::GestureEvent event( - 0, - 0, - 0, - ui::EventTimeForNow(), - ui::GestureEventDetails(ui::ET_GESTURE_WIN8_EDGE_SWIPE)); - SendEventToProcessor(&event); -} - -void RemoteWindowTreeHostWin::OnTouchDown(int32_t x, - int32_t y, - uint64_t timestamp, - uint32_t pointer_id) { - gfx::Point location = PointFromNativeEvent(x, y); - ui::TouchEvent event(ui::ET_TOUCH_PRESSED, - location, - pointer_id, - base::TimeDelta::FromMicroseconds(timestamp)); - SendEventToProcessor(&event); -} - -void RemoteWindowTreeHostWin::OnTouchUp(int32_t x, - int32_t y, - uint64_t timestamp, - uint32_t pointer_id) { - gfx::Point location = PointFromNativeEvent(x, y); - ui::TouchEvent event(ui::ET_TOUCH_RELEASED, - location, - pointer_id, - base::TimeDelta::FromMicroseconds(timestamp)); - SendEventToProcessor(&event); -} - -void RemoteWindowTreeHostWin::OnTouchMoved(int32_t x, - int32_t y, - uint64_t timestamp, - uint32_t pointer_id) { - gfx::Point location = PointFromNativeEvent(x, y); - ui::TouchEvent event(ui::ET_TOUCH_MOVED, - location, - pointer_id, - base::TimeDelta::FromMicroseconds(timestamp)); - SendEventToProcessor(&event); -} - -void RemoteWindowTreeHostWin::OnSetCursorPosAck() { - DCHECK_GT(ignore_mouse_moves_until_set_cursor_ack_, 0); - ignore_mouse_moves_until_set_cursor_ack_--; -} - -ui::RemoteInputMethodPrivateWin* -RemoteWindowTreeHostWin::GetRemoteInputMethodPrivate() { - return ui::RemoteInputMethodPrivateWin::Get(GetInputMethod()); -} - -void RemoteWindowTreeHostWin::OnImeCandidatePopupChanged(bool visible) { - ui::RemoteInputMethodPrivateWin* remote_input_method_private = - GetRemoteInputMethodPrivate(); - if (!remote_input_method_private) - return; - remote_input_method_private->OnCandidatePopupChanged(visible); -} - -void RemoteWindowTreeHostWin::OnImeCompositionChanged( - const base::string16& text, - int32_t selection_start, - int32_t selection_end, - const std::vector<metro_viewer::UnderlineInfo>& underlines) { - ui::RemoteInputMethodPrivateWin* remote_input_method_private = - GetRemoteInputMethodPrivate(); - if (!remote_input_method_private) - return; - ui::CompositionText composition_text; - FillCompositionText( - text, selection_start, selection_end, underlines, &composition_text); - remote_input_method_private->OnCompositionChanged(composition_text); -} - -void RemoteWindowTreeHostWin::OnImeTextCommitted(const base::string16& text) { - ui::RemoteInputMethodPrivateWin* remote_input_method_private = - GetRemoteInputMethodPrivate(); - if (!remote_input_method_private) - return; - remote_input_method_private->OnTextCommitted(text); -} - -void RemoteWindowTreeHostWin::OnImeInputSourceChanged(uint16_t language_id, - bool is_ime) { - ui::RemoteInputMethodPrivateWin* remote_input_method_private = - GetRemoteInputMethodPrivate(); - if (!remote_input_method_private) - return; - remote_input_method_private->OnInputSourceChanged(language_id, is_ime); -} - -void RemoteWindowTreeHostWin::DispatchKeyboardMessage(ui::EventType type, - uint32_t vkey, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags, - bool is_character) { - SetEventFlags(flags | mouse_event_flags()); - if (base::MessageLoop::current()->IsNested()) { - int index = (flags & ui::EF_ALT_DOWN) ? 1 : 0; - const int char_message[] = {WM_CHAR, WM_SYSCHAR}; - const int keydown_message[] = {WM_KEYDOWN, WM_SYSKEYDOWN}; - const int keyup_message[] = {WM_KEYUP, WM_SYSKEYUP}; - uint32_t message = - is_character ? char_message[index] - : (type == ui::ET_KEY_PRESSED ? keydown_message[index] - : keyup_message[index]); - ::PostThreadMessage(::GetCurrentThreadId(), - message, - vkey, - repeat_count | scan_code >> 15); - } else if (is_character) { - ui::KeyEvent event(static_cast<base::char16>(vkey), - ui::KeyboardCodeForWindowsKeyCode(vkey), - flags); - SendEventToProcessor(&event); - } else { - ui::KeyEvent event(type, - ui::KeyboardCodeForWindowsKeyCode(vkey), - flags); - SendEventToProcessor(&event); - } -} - -void RemoteWindowTreeHostWin::SetEventFlags(uint32_t flags) { - if (flags == event_flags_) - return; - event_flags_ = flags; - SetVirtualKeyStates(event_flags_); -} - -} // namespace aura diff --git a/chromium/ui/aura/remote_window_tree_host_win.h b/chromium/ui/aura/remote_window_tree_host_win.h deleted file mode 100644 index 4614fd6e8aa..00000000000 --- a/chromium/ui/aura/remote_window_tree_host_win.h +++ /dev/null @@ -1,197 +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_AURA_REMOTE_WINDOW_TREE_HOST_WIN_H_ -#define UI_AURA_REMOTE_WINDOW_TREE_HOST_WIN_H_ - -#include <stdint.h> - -#include <vector> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "ui/aura/window_tree_host.h" -#include "ui/base/ime/remote_input_method_delegate_win.h" -#include "ui/events/event.h" -#include "ui/events/event_constants.h" -#include "ui/events/event_source.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/metro_viewer/ime_types.h" - -struct MetroViewerHostMsg_MouseButtonParams; - -namespace base { -class FilePath; -} - -namespace ui { -class RemoteInputMethodPrivateWin; -class ViewProp; -} - -namespace IPC { -class Message; -class Sender; -} - -namespace aura { - -// WindowTreeHost implementaton that receives events from a different -// process. In the case of Windows this is the Windows 8 (aka Metro) -// frontend process, which forwards input events to this class. -class AURA_EXPORT RemoteWindowTreeHostWin - : public WindowTreeHost, - public ui::internal::RemoteInputMethodDelegateWin { - public: - // Returns the current RemoteWindowTreeHostWin. This does *not* create a - // RemoteWindowTreeHostWin. - static RemoteWindowTreeHostWin* Instance(); - - // Returns true if there is a RemoteWindowTreeHostWin and it has a valid - // HWND. A return value of false typically indicates we're not in metro mode. - static bool IsValid(); - - // Sets the handle to the remote window. The |remote_window| is the actual - // window owned by the viewer process. Call this before Connected() for some - // customers like input method initialization which needs the handle. - void SetRemoteWindowHandle(HWND remote_window); - HWND remote_window() { return remote_window_; } - - // The |host| can be used when we need to send a message to it. - void Connected(IPC::Sender* host); - // Called when the remote process has closed its IPC connection. - void Disconnected(); - - // Called when we have a message from the remote process. - bool OnMessageReceived(const IPC::Message& message); - - void HandleOpenURLOnDesktop(const base::FilePath& shortcut, - const base::string16& url); - - void HandleWindowSizeChanged(uint32_t width, uint32_t height); - - // Returns the active ASH root window. - Window* GetAshWindow(); - - // Returns true if the remote window is the foreground window according to the - // OS. - bool IsForegroundWindow(); - - protected: - RemoteWindowTreeHostWin(); - ~RemoteWindowTreeHostWin() override; - - private: - // IPC message handing methods: - void OnMouseMoved(int32_t x, int32_t y, int32_t flags); - void OnMouseButton(const MetroViewerHostMsg_MouseButtonParams& params); - void OnKeyDown(uint32_t vkey, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags); - void OnKeyUp(uint32_t vkey, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags); - void OnChar(uint32_t key_code, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags); - void OnWindowActivated(bool repaint); - void OnEdgeGesture(); - void OnTouchDown(int32_t x, - int32_t y, - uint64_t timestamp, - uint32_t pointer_id); - void OnTouchUp(int32_t x, int32_t y, uint64_t timestamp, uint32_t pointer_id); - void OnTouchMoved(int32_t x, - int32_t y, - uint64_t timestamp, - uint32_t pointer_id); - void OnSetCursorPosAck(); - - // For Input Method support: - ui::RemoteInputMethodPrivateWin* GetRemoteInputMethodPrivate(); - void OnImeCandidatePopupChanged(bool visible); - void OnImeCompositionChanged( - const base::string16& text, - int32_t selection_start, - int32_t selection_end, - const std::vector<metro_viewer::UnderlineInfo>& underlines); - void OnImeTextCommitted(const base::string16& text); - void OnImeInputSourceChanged(uint16_t language_id, bool is_ime); - - // WindowTreeHost overrides: - ui::EventSource* GetEventSource() override; - gfx::AcceleratedWidget GetAcceleratedWidget() override; - void ShowImpl() override; - void HideImpl() override; - gfx::Rect GetBounds() const override; - void SetBounds(const gfx::Rect& bounds) override; - gfx::Point GetLocationOnNativeScreen() const override; - void SetCapture() override; - void ReleaseCapture() override; - void SetCursorNative(gfx::NativeCursor cursor) override; - void MoveCursorToNative(const gfx::Point& location) override; - void OnCursorVisibilityChangedNative(bool show) override; - - // ui::internal::RemoteInputMethodDelegateWin overrides: - void CancelComposition() override; - void OnTextInputClientUpdated( - const std::vector<int32_t>& input_scopes, - const std::vector<gfx::Rect>& composition_character_bounds) override; - - // Helper function to dispatch a keyboard message to the desired target. - // The default target is the WindowEventDispatcher. For nested message loop - // invocations we post a synthetic keyboard message directly into the message - // loop. The dispatcher for the nested loop would then decide how this - // message is routed. - void DispatchKeyboardMessage(ui::EventType type, - uint32_t vkey, - uint32_t repeat_count, - uint32_t scan_code, - uint32_t flags, - bool is_character); - - // Sets the event flags. |flags| is a bitmask of EventFlags. If there is a - // change the system virtual key state is updated as well. This way if chrome - // queries for key state it matches that of event being dispatched. - void SetEventFlags(uint32_t flags); - - uint32_t mouse_event_flags() const { - return event_flags_ & (ui::EF_LEFT_MOUSE_BUTTON | - ui::EF_MIDDLE_MOUSE_BUTTON | - ui::EF_RIGHT_MOUSE_BUTTON); - } - - uint32_t key_event_flags() const { - return event_flags_ & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | - ui::EF_ALT_DOWN | ui::EF_CAPS_LOCK_ON); - } - - HWND remote_window_; - IPC::Sender* host_; - scoped_ptr<ui::ViewProp> prop_; - - // Incremented if we need to ignore mouse messages until the SetCursorPos - // operation is acked by the viewer. - int ignore_mouse_moves_until_set_cursor_ack_; - - // Tracking last click event for synthetically generated mouse events. - scoped_ptr<ui::MouseEvent> last_mouse_click_event_; - - // State of the keyboard/mouse at the time of the last input event. See - // description of SetEventFlags(). - uint32_t event_flags_; - - // Current size of this root window. - gfx::Size window_size_; - - DISALLOW_COPY_AND_ASSIGN(RemoteWindowTreeHostWin); -}; - -} // namespace aura - -#endif // UI_AURA_REMOTE_WINDOW_TREE_HOST_WIN_H_ diff --git a/chromium/ui/aura/window.cc b/chromium/ui/aura/window.cc index bd9f023042f..0b848e9fb50 100644 --- a/chromium/ui/aura/window.cc +++ b/chromium/ui/aura/window.cc @@ -67,8 +67,7 @@ class ScopedCursorHider { client::CursorClient* cursor_client = client::GetCursorClient(window_); if (cursor_client) { const gfx::Display& display = - gfx::Screen::GetScreenFor(window_)->GetDisplayNearestWindow( - window_); + gfx::Screen::GetScreen()->GetDisplayNearestWindow(window_); cursor_client->SetDisplay(display); cursor_client->ShowCursor(); } diff --git a/chromium/ui/aura/window_event_dispatcher.cc b/chromium/ui/aura/window_event_dispatcher.cc index d9c59136b24..39dc1a48937 100644 --- a/chromium/ui/aura/window_event_dispatcher.cc +++ b/chromium/ui/aura/window_event_dispatcher.cc @@ -107,14 +107,11 @@ void WindowEventDispatcher::RepostEvent(const ui::LocatedEvent* event) { // We allow for only one outstanding repostable event. This is used // in exiting context menus. A dropped repost request is allowed. if (event->type() == ui::ET_MOUSE_PRESSED) { - held_repostable_event_.reset( - new ui::MouseEvent( - static_cast<const ui::MouseEvent&>(*event), - static_cast<aura::Window*>(event->target()), - window())); + held_repostable_event_.reset(new ui::MouseEvent( + *event->AsMouseEvent(), static_cast<aura::Window*>(event->target()), + window())); } else if (event->type() == ui::ET_TOUCH_PRESSED) { - held_repostable_event_.reset( - new ui::TouchEvent(static_cast<const ui::TouchEvent&>(*event))); + held_repostable_event_.reset(new ui::TouchEvent(*event->AsTouchEvent())); } else { DCHECK(event->type() == ui::ET_GESTURE_TAP_DOWN); held_repostable_event_.reset(); @@ -474,14 +471,11 @@ ui::EventDispatchDetails WindowEventDispatcher::PreDispatchEvent( DispatchDetails details; if (event->IsMouseEvent()) { - details = PreDispatchMouseEvent(target_window, - static_cast<ui::MouseEvent*>(event)); + details = PreDispatchMouseEvent(target_window, event->AsMouseEvent()); } else if (event->IsScrollEvent()) { - details = PreDispatchLocatedEvent(target_window, - static_cast<ui::ScrollEvent*>(event)); + details = PreDispatchLocatedEvent(target_window, event->AsScrollEvent()); } else if (event->IsTouchEvent()) { - details = PreDispatchTouchEvent(target_window, - static_cast<ui::TouchEvent*>(event)); + details = PreDispatchTouchEvent(target_window, event->AsTouchEvent()); } if (details.dispatcher_destroyed || details.target_destroyed) return details; @@ -508,8 +502,7 @@ ui::EventDispatchDetails WindowEventDispatcher::PostDispatchEvent( // being dispatched. if (is_dispatched_held_event(event) || !held_move_event_ || !held_move_event_->IsTouchEvent()) { - const ui::TouchEvent& touchevent = - static_cast<const ui::TouchEvent&>(event); + const ui::TouchEvent& touchevent = *event.AsTouchEvent(); if (!touchevent.synchronous_handling_disabled()) { scoped_ptr<ui::GestureRecognizer::Gestures> gestures; diff --git a/chromium/ui/aura/window_event_dispatcher_unittest.cc b/chromium/ui/aura/window_event_dispatcher_unittest.cc index c8ae024b1bd..ef992595830 100644 --- a/chromium/ui/aura/window_event_dispatcher_unittest.cc +++ b/chromium/ui/aura/window_event_dispatcher_unittest.cc @@ -2633,7 +2633,7 @@ TEST_F(WindowEventDispatcherTest, TouchMovesMarkedWhenCausingScroll) { ui::ET_TOUCH_RELEASED, location + gfx::Vector2d(200, 200), 0, ui::EventTimeForNow() + base::TimeDelta::FromSeconds(1)); DispatchEventUsingWindowDispatcher(&release); - EXPECT_FALSE(recorder.LastTouchMayCauseScrolling()); + EXPECT_TRUE(recorder.LastTouchMayCauseScrolling()); EXPECT_TRUE(recorder.HasReceivedEvent(ui::ET_TOUCH_RELEASED)); EXPECT_TRUE(recorder.HasReceivedEvent(ui::ET_GESTURE_SCROLL_END)); diff --git a/chromium/ui/aura/window_targeter.cc b/chromium/ui/aura/window_targeter.cc index 4b720518e5b..96413d02538 100644 --- a/chromium/ui/aura/window_targeter.cc +++ b/chromium/ui/aura/window_targeter.cc @@ -62,10 +62,9 @@ bool WindowTargeter::EventLocationInsideBounds( ui::EventTarget* WindowTargeter::FindTargetForEvent(ui::EventTarget* root, ui::Event* event) { Window* window = static_cast<Window*>(root); - Window* target = - event->IsKeyEvent() - ? FindTargetForKeyEvent(window, *static_cast<ui::KeyEvent*>(event)) - : FindTargetForNonKeyEvent(window, event); + Window* target = event->IsKeyEvent() + ? FindTargetForKeyEvent(window, *event->AsKeyEvent()) + : FindTargetForNonKeyEvent(window, event); if (target && !window->parent() && !window->Contains(target)) { // |window| is the root window, but |target| is not a descendent of // |window|. So do not allow dispatching from here. Instead, dispatch the @@ -148,7 +147,7 @@ Window* WindowTargeter::FindTargetInRootWindow(Window* root_window, if (event.IsTouchEvent()) { // Query the gesture-recognizer to find targets for touch events. - const ui::TouchEvent& touch = static_cast<const ui::TouchEvent&>(event); + const ui::TouchEvent& touch = *event.AsTouchEvent(); ui::GestureConsumer* consumer = ui::GestureRecognizer::Get()->GetTouchLockedTarget(touch); if (consumer) diff --git a/chromium/ui/aura/window_tree_host.cc b/chromium/ui/aura/window_tree_host.cc index bb988e1f86d..aa6fb73ec12 100644 --- a/chromium/ui/aura/window_tree_host.cc +++ b/chromium/ui/aura/window_tree_host.cc @@ -32,8 +32,8 @@ const char kWindowTreeHostForAcceleratedWidget[] = "__AURA_WINDOW_TREE_HOST_ACCELERATED_WIDGET__"; float GetDeviceScaleFactorFromDisplay(Window* window) { - gfx::Display display = gfx::Screen::GetScreenFor(window)-> - GetDisplayNearestWindow(window); + gfx::Display display = + gfx::Screen::GetScreen()->GetDisplayNearestWindow(window); DCHECK(display.is_valid()); return display.device_scale_factor(); } @@ -314,7 +314,7 @@ void WindowTreeHost::MoveCursorToInternal(const gfx::Point& root_location, client::CursorClient* cursor_client = client::GetCursorClient(window()); if (cursor_client) { const gfx::Display& display = - gfx::Screen::GetScreenFor(window())->GetDisplayNearestWindow(window()); + gfx::Screen::GetScreen()->GetDisplayNearestWindow(window()); cursor_client->SetDisplay(display); } dispatcher()->OnCursorMovedToRootLocation(root_location); diff --git a/chromium/ui/aura/window_tree_host_mac.h b/chromium/ui/aura/window_tree_host_mac.h index eefb7e1e52b..d8adb74e80d 100644 --- a/chromium/ui/aura/window_tree_host_mac.h +++ b/chromium/ui/aura/window_tree_host_mac.h @@ -20,10 +20,6 @@ class MouseEvent; namespace aura { -namespace internal { -class TouchEventCalibrate; -} - class AURA_EXPORT WindowTreeHostMac : public WindowTreeHost { public: explicit WindowTreeHostMac(const gfx::Rect& bounds); diff --git a/chromium/ui/aura/window_tree_host_platform.cc b/chromium/ui/aura/window_tree_host_platform.cc index 1e4baf6392e..8dfa4d05327 100644 --- a/chromium/ui/aura/window_tree_host_platform.cc +++ b/chromium/ui/aura/window_tree_host_platform.cc @@ -83,7 +83,7 @@ void WindowTreeHostPlatform::HideImpl() { } gfx::Rect WindowTreeHostPlatform::GetBounds() const { - return window_->GetBounds(); + return window_ ? window_->GetBounds() : gfx::Rect(); } void WindowTreeHostPlatform::SetBounds(const gfx::Rect& bounds) { @@ -125,7 +125,7 @@ void WindowTreeHostPlatform::OnCursorVisibilityChangedNative(bool show) { void WindowTreeHostPlatform::OnBoundsChanged(const gfx::Rect& new_bounds) { float current_scale = compositor()->device_scale_factor(); - float new_scale = gfx::Screen::GetScreenFor(window()) + float new_scale = gfx::Screen::GetScreen() ->GetDisplayNearestWindow(window()) .device_scale_factor(); gfx::Rect old_bounds = bounds_; diff --git a/chromium/ui/aura/window_tree_host_x11.cc b/chromium/ui/aura/window_tree_host_x11.cc index ebcb154e60b..a5675e9f3ce 100644 --- a/chromium/ui/aura/window_tree_host_x11.cc +++ b/chromium/ui/aura/window_tree_host_x11.cc @@ -45,6 +45,7 @@ #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/platform/platform_event_observer.h" +#include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/screen.h" @@ -103,107 +104,6 @@ bool default_override_redirect = false; } // namespace -namespace internal { - -// TODO(miletus) : Move this into DeviceDataManager. -// Accomplishes 2 tasks concerning touch event calibration: -// 1. Being a message-pump observer, -// routes all the touch events to the X root window, -// where they can be calibrated later. -// 2. Has the Calibrate method that does the actual bezel calibration, -// when invoked from X root window's event dispatcher. -class TouchEventCalibrate : public ui::PlatformEventObserver { - public: - TouchEventCalibrate() : left_(0), right_(0), top_(0), bottom_(0) { - if (ui::PlatformEventSource::GetInstance()) - ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); - std::vector<std::string> parts = base::SplitString( - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kTouchCalibration), - ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - if (parts.size() >= 4) { - if (!base::StringToInt(parts[0], &left_)) - DLOG(ERROR) << "Incorrect left border calibration value passed."; - if (!base::StringToInt(parts[1], &right_)) - DLOG(ERROR) << "Incorrect right border calibration value passed."; - if (!base::StringToInt(parts[2], &top_)) - DLOG(ERROR) << "Incorrect top border calibration value passed."; - if (!base::StringToInt(parts[3], &bottom_)) - DLOG(ERROR) << "Incorrect bottom border calibration value passed."; - } - } - - ~TouchEventCalibrate() override { - if (ui::PlatformEventSource::GetInstance()) - ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this); - } - - // Modify the location of the |event|, - // expanding it from |bounds| to (|bounds| + bezels). - // Required when touchscreen is bigger than screen (i.e. has bezels), - // because we receive events in touchscreen coordinates, - // which need to be expanded when converting to screen coordinates, - // so that location on bezels will be outside of screen area. - void Calibrate(ui::TouchEvent* event, const gfx::Rect& bounds) { - int x = event->x(); - int y = event->y(); - - if (!left_ && !right_ && !top_ && !bottom_) - return; - - const int resolution_x = bounds.width(); - const int resolution_y = bounds.height(); - if (left_ || right_) { - // Offset the x position to the real - x -= left_; - // Scale the screen area back to the full resolution of the screen. - x = (x * resolution_x) / (resolution_x - (right_ + left_)); - } - if (top_ || bottom_) { - // When there is a top bezel we add our border, - y -= top_; - // Scale the screen area back to the full resolution of the screen. - y = (y * resolution_y) / (resolution_y - (bottom_ + top_)); - } - - // Set the modified coordinate back to the event. - if (event->root_location() == event->location()) { - // Usually those will be equal, - // if not, I am not sure what the correct value should be. - event->set_root_location(gfx::Point(x, y)); - } - event->set_location(gfx::Point(x, y)); - } - - private: - // ui::PlatformEventObserver: - void WillProcessEvent(const ui::PlatformEvent& event) override { - if (event->type == GenericEvent && - (event->xgeneric.evtype == XI_TouchBegin || - event->xgeneric.evtype == XI_TouchUpdate || - event->xgeneric.evtype == XI_TouchEnd)) { - XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data); - xievent->event = xievent->root; - xievent->event_x = xievent->root_x; - xievent->event_y = xievent->root_y; - } - } - - void DidProcessEvent(const ui::PlatformEvent& event) override {} - - // The difference in screen's native resolution pixels between - // the border of the touchscreen and the border of the screen, - // aka bezel sizes. - int left_; - int right_; - int top_; - int bottom_; - - DISALLOW_COPY_AND_ASSIGN(TouchEventCalibrate); -}; - -} // namespace internal - //////////////////////////////////////////////////////////////////////////////// // WindowTreeHostX11 @@ -214,7 +114,6 @@ WindowTreeHostX11::WindowTreeHostX11(const gfx::Rect& bounds) current_cursor_(ui::kCursorNull), window_mapped_(false), bounds_(bounds), - touch_calibrate_(new internal::TouchEventCalibrate), atom_cache_(xdisplay_, kAtomsToCache) { XSetWindowAttributes swa; memset(&swa, 0, sizeof(swa)); @@ -358,8 +257,8 @@ uint32_t WindowTreeHostX11::DispatchEvent(const ui::PlatformEvent& event) { client::CursorClient* cursor_client = client::GetCursorClient(root_window); if (cursor_client) { - const gfx::Display display = gfx::Screen::GetScreenFor( - root_window)->GetDisplayNearestWindow(root_window); + const gfx::Display display = + gfx::Screen::GetScreen()->GetDisplayNearestWindow(root_window); cursor_client->SetDisplay(display); } // EnterNotify creates ET_MOUSE_MOVE. Mark as synthesized as this is @@ -500,8 +399,9 @@ void WindowTreeHostX11::SetBounds(const gfx::Rect& bounds) { // Even if the host window's size doesn't change, aura's root window // size, which is in DIP, changes when the scale changes. float current_scale = compositor()->device_scale_factor(); - float new_scale = gfx::Screen::GetScreenFor(window())-> - GetDisplayNearestWindow(window()).device_scale_factor(); + float new_scale = gfx::Screen::GetScreen() + ->GetDisplayNearestWindow(window()) + .device_scale_factor(); bool origin_changed = bounds_.origin() != bounds.origin(); bool size_changed = bounds_.size() != bounds.size(); XWindowChanges changes = {0}; @@ -572,7 +472,6 @@ void WindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) { void WindowTreeHostX11::DispatchXI2Event(const base::NativeEvent& event) { ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); XEvent* xev = event; - XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data); if (!factory->ShouldProcessXI2Event(xev)) return; @@ -599,10 +498,6 @@ void WindowTreeHostX11::DispatchXI2Event(const base::NativeEvent& event) { case ui::ET_TOUCH_CANCELLED: case ui::ET_TOUCH_RELEASED: { ui::TouchEvent touchev(xev); - if (ui::DeviceDataManagerX11::GetInstance()->TouchEventNeedsCalibrate( - xiev->deviceid)) { - touch_calibrate_->Calibrate(&touchev, bounds_); - } TranslateAndDispatchLocatedEvent(&touchev); break; } diff --git a/chromium/ui/aura/window_tree_host_x11.h b/chromium/ui/aura/window_tree_host_x11.h index 906f605a9b0..0587bb6fa08 100644 --- a/chromium/ui/aura/window_tree_host_x11.h +++ b/chromium/ui/aura/window_tree_host_x11.h @@ -27,13 +27,8 @@ class MouseEvent; namespace aura { -namespace internal { -class TouchEventCalibrate; -} - class AURA_EXPORT WindowTreeHostX11 : public WindowTreeHost, public ui::PlatformEventDispatcher { - public: explicit WindowTreeHostX11(const gfx::Rect& bounds); ~WindowTreeHostX11() override; @@ -95,8 +90,6 @@ class AURA_EXPORT WindowTreeHostX11 : public WindowTreeHost, // The bounds of |xwindow_|. gfx::Rect bounds_; - scoped_ptr<internal::TouchEventCalibrate> touch_calibrate_; - ui::X11AtomCache atom_cache_; DISALLOW_COPY_AND_ASSIGN(WindowTreeHostX11); diff --git a/chromium/ui/aura/window_unittest.cc b/chromium/ui/aura/window_unittest.cc index 60792c2a4b7..f2699eb8b0e 100644 --- a/chromium/ui/aura/window_unittest.cc +++ b/chromium/ui/aura/window_unittest.cc @@ -358,19 +358,19 @@ TEST_F(WindowTest, MoveCursorTo) { Window* root = root_window(); root->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("10,10", - gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); w1->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("20,20", - gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); w11->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("25,25", - gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); w111->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("30,30", - gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); w1111->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("35,35", - gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); } TEST_F(WindowTest, ContainsMouse) { @@ -398,8 +398,8 @@ TEST_F(WindowTest, MoveCursorToWithTransformRootWindow) { // TODO(yoshiki): fix this to build on Windows. See crbug.com/133413.OD EXPECT_EQ("50,120", QueryLatestMousePositionRequestInHost(host()).ToString()); #endif - EXPECT_EQ("10,10", gfx::Screen::GetScreenFor( - root_window())->GetCursorScreenPoint().ToString()); + EXPECT_EQ("10,10", + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); } // Tests Window::ConvertPointToWindow() with transform to non-root windows. @@ -413,21 +413,21 @@ TEST_F(WindowTest, MoveCursorToWithTransformWindow) { w1->SetTransform(transform1); w1->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("30,30", - gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); gfx::Transform transform2; transform2.Translate(-10, 20); w1->SetTransform(transform2); w1->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("10,40", - gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); gfx::Transform transform3; transform3.Rotate(90.0); w1->SetTransform(transform3); w1->MoveCursorTo(gfx::Point(5, 5)); EXPECT_EQ("5,15", - gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); gfx::Transform transform4; transform4.Translate(100.0, 100.0); @@ -436,7 +436,7 @@ TEST_F(WindowTest, MoveCursorToWithTransformWindow) { w1->SetTransform(transform4); w1->MoveCursorTo(gfx::Point(10, 10)); EXPECT_EQ("60,130", - gfx::Screen::GetScreenFor(w1.get())->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); } // Test Window::ConvertPointToWindow() with complex transforms to both root and @@ -453,8 +453,6 @@ TEST_F(WindowTest, MoveCursorToWithComplexTransform) { scoped_ptr<Window> w1111( CreateTestWindow(SK_ColorRED, 1111, gfx::Rect(5, 5, 50, 50), w111.get())); - Window* root = root_window(); - // The root window expects transforms that produce integer rects. gfx::Transform root_transform; root_transform.Translate(60.0, 70.0); @@ -479,7 +477,7 @@ TEST_F(WindowTest, MoveCursorToWithComplexTransform) { EXPECT_EQ("169,80", QueryLatestMousePositionRequestInHost(host()).ToString()); #endif EXPECT_EQ("20,53", - gfx::Screen::GetScreenFor(root)->GetCursorScreenPoint().ToString()); + gfx::Screen::GetScreen()->GetCursorScreenPoint().ToString()); } // Tests that we do not crash when a Window is destroyed by going out of @@ -1588,7 +1586,7 @@ TEST_F(WindowTest, IgnoreEventsTest) { TEST_F(WindowTest, Transform) { gfx::Size size = host()->GetBounds().size(); EXPECT_EQ(gfx::Rect(size), - gfx::Screen::GetScreenFor(root_window())->GetDisplayNearestPoint( + gfx::Screen::GetScreen()->GetDisplayNearestPoint( gfx::Point()).bounds()); // Rotate it clock-wise 90 degrees. @@ -1603,7 +1601,7 @@ TEST_F(WindowTest, Transform) { root_window()->bounds().size().ToString()); EXPECT_EQ( gfx::Rect(transformed_size).ToString(), - gfx::Screen::GetScreenFor(root_window())->GetDisplayNearestPoint( + gfx::Screen::GetScreen()->GetDisplayNearestPoint( gfx::Point()).bounds().ToString()); // Host size shouldn't change. diff --git a/chromium/ui/base/BUILD.gn b/chromium/ui/base/BUILD.gn index 6190b065df1..db554383406 100644 --- a/chromium/ui/base/BUILD.gn +++ b/chromium/ui/base/BUILD.gn @@ -2,18 +2,56 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/buildflag_header.gni") import("//build/config/compiler/compiler.gni") import("//build/config/sanitizers/sanitizers.gni") import("//build/config/ui.gni") import("//testing/test.gni") +import("//ui/base/ui_features.gni") +import("//ui/ozone/ozone.gni") if (is_android) { import("//build/config/android/config.gni") import("//build/config/android/rules.gni") +} else if (is_mac) { + import("//build/config/mac/rules.gni") } build_ime = !is_ios +# As part of building Chrome on iOS, it is necessary to run a tool on +# the host to load datapack and generate output in a format defined +# by the platform (this is to support notifications). +# +# Introduce a standalone target that build on both 'host' and 'target' +# toolset that just build the support to load datapack. The dependency +# should be kept minimal to have to build too many targets with multiple +# toolsets. +component("ui_data_pack") { + sources = [ + "resource/data_pack.cc", + "resource/data_pack.h", + "resource/data_pack_export.h", + "resource/resource_handle.h", + "resource/scale_factor.cc", + "resource/scale_factor.h", + ] + + deps = [ + "//base", + ] + + defines = [ "UI_DATA_PACK_IMPLEMENTATION" ] + + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] +} + +buildflag_header("ui_features") { + header = "ui_features.h" + flags = [ "ENABLE_HIDPI=$enable_hidpi" ] +} + component("base") { output_name = "ui_base" @@ -30,6 +68,8 @@ component("base") { "clipboard/clipboard_mac.h", "clipboard/clipboard_mac.mm", "clipboard/clipboard_types.h", + "clipboard/clipboard_util_mac.h", + "clipboard/clipboard_util_mac.mm", "clipboard/clipboard_util_win.cc", "clipboard/clipboard_util_win.h", "clipboard/clipboard_win.cc", @@ -101,6 +141,7 @@ component("base") { "cursor/cursor_android.cc", "cursor/cursor_loader.h", "cursor/cursor_win.cc", + "default_style.h", "default_theme_provider_mac.mm", "device_form_factor.h", "device_form_factor_android.cc", @@ -148,6 +189,8 @@ component("base") { "layout.cc", "layout.h", "layout_mac.mm", + "material_design/material_design_controller.cc", + "material_design/material_design_controller.h", "models/button_menu_item_model.cc", "models/button_menu_item_model.h", "models/combobox_model.cc", @@ -178,10 +221,6 @@ component("base") { "nine_image_painter_factory.h", "page_transition_types.cc", "page_transition_types.h", - "resource/data_pack.cc", - "resource/data_pack.h", - "resource/material_design/material_design_controller.cc", - "resource/material_design/material_design_controller.h", "resource/resource_bundle.cc", "resource/resource_bundle.h", "resource/resource_bundle_android.cc", @@ -192,7 +231,6 @@ component("base") { "resource/resource_bundle_win.h", "resource/resource_data_dll_win.cc", "resource/resource_data_dll_win.h", - "resource/resource_handle.h", "template_expressions.cc", "template_expressions.h", "theme_provider.cc", @@ -318,6 +356,7 @@ component("base") { defines = [ "UI_BASE_IMPLEMENTATION" ] public_deps = [ + ":ui_features", "//base", "//skia", "//ui/events:events_base", @@ -331,6 +370,7 @@ component("base") { "//base/third_party/dynamic_annotations", "//net", "//third_party/icu", + "//ui/base:ui_data_pack", "//ui/events", "//ui/events/devices", "//ui/resources", @@ -371,7 +411,7 @@ component("base") { "dragdrop/drag_utils_aura.cc", ] } - if (use_x11) { + if (use_x11 || ozone_platform_x11) { sources += [ "x/x11_foreign_window_manager.cc", "x/x11_foreign_window_manager.h", @@ -479,7 +519,7 @@ component("base") { ] } - if (use_x11) { + if (use_x11 || ozone_platform_x11) { #'all_dependent_settings': { #'ldflags': [ #'-L<(PRODUCT_DIR)', @@ -517,6 +557,7 @@ component("base") { ] deps += [ + "//ui/events/ozone:events_ozone_evdev", "//ui/events/ozone:events_ozone_layout", "//ui/ozone:ozone_base", ] @@ -599,6 +640,8 @@ source_set("test_support") { "test/scoped_fake_nswindow_focus.mm", "test/scoped_fake_nswindow_fullscreen.h", "test/scoped_fake_nswindow_fullscreen.mm", + "test/scoped_preferred_scroller_style_mac.h", + "test/scoped_preferred_scroller_style_mac.mm", "test/test_clipboard.cc", "test/test_clipboard.h", "test/windowed_nsnotification_observer.h", @@ -621,6 +664,7 @@ source_set("test_support") { "//base/test:test_config", "//skia", "//testing/gtest", + "//ui/base:ui_data_pack", "//ui/events:events_base", "//ui/events:test_support", "//ui/gfx", @@ -635,7 +679,10 @@ source_set("test_support") { "ime/dummy_text_input_client.h", ] - deps += [ "//ui/base/ime" ] + deps += [ + "//ui/base/ime", + "//ui/events", + ] } if (!use_aura) { @@ -659,30 +706,33 @@ if (is_android) { } } -# -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("ui_base_unittests_run") { +bundle_data("ui_base_unittests_bundle_data") { testonly = true - deps = [ - ":ui_base_unittests", + sources = [ + "test/data/data_pack_unittest/truncated-header.pak", + ] + outputs = [ + "{{bundle_resources_dir}}/" + + "{{source_root_relative_dir}}/{{source_file_part}}", ] } # GYP version: ui/base/ui_base_tests.gyp:ui_base_unittests test("ui_base_unittests") { sources = [ + "clipboard/clipboard_util_mac_unittest.mm", "ios/cru_context_menu_controller_unittest.mm", "l10n/l10n_util_mac_unittest.mm", "l10n/l10n_util_unittest.cc", "l10n/l10n_util_win_unittest.cc", "l10n/time_format_unittest.cc", "layout_unittest.cc", + "material_design/material_design_controller_unittest.cc", "models/tree_node_iterator_unittest.cc", "resource/data_pack_literal.cc", "resource/data_pack_unittest.cc", - "resource/material_design/material_design_controller_unittest.cc", "resource/resource_bundle_unittest.cc", + "resource/scale_factor_unittest.cc", "template_expressions_unittest.cc", "test/run_all_unittests.cc", "user_activity/user_activity_detector_unittest.cc", @@ -743,7 +793,6 @@ test("ui_base_unittests") { "ime/composition_text_unittest.cc", "ime/input_method_base_unittest.cc", "ime/input_method_chromeos_unittest.cc", - "ime/remote_input_method_win_unittest.cc", "ime/win/imm32_manager_unittest.cc", "ime/win/tsf_input_scope_unittest.cc", ] @@ -766,8 +815,8 @@ test("ui_base_unittests") { configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] deps = [ + ":ui_base_unittests_bundle_data", "//base", - "//base/allocator", "//base/test:test_support", "//net", "//skia", @@ -776,6 +825,7 @@ test("ui_base_unittests") { "//third_party/icu", "//ui/base", "//ui/base:test_support", + "//ui/base:ui_data_pack", "//ui/events:events_base", "//ui/events:test_support", "//ui/gfx:test_support", @@ -785,14 +835,14 @@ test("ui_base_unittests") { "//url", ] - if (is_ios) { - # TODO(GYP) lots of iOS-only steps for ui_base_unittests - } - if (build_ime) { deps += [ "//ui/base/ime" ] } + if (is_ios) { + deps += [ "//ui/resources:ui_test_pak_bundle_data" ] + } + if (is_win) { sources += [ "dragdrop/os_exchange_data_win_unittest.cc", @@ -843,8 +893,8 @@ test("ui_base_unittests") { if (is_mac) { deps += [ + ":ui_unittests Framework", "//third_party/mozilla", - #'ui_base_tests_bundle', TODO(GYP) ] } @@ -899,4 +949,13 @@ test("ui_base_unittests") { } } } -# TODO(GYP) Mac (ui_base_tests_bundle) + +if (is_mac) { + mac_framework("ui_unittests Framework") { + testonly = true + deps = [ + "//ui/resources:ui_test_pak_bundle_data", + ] + info_plist = "test/framework-Info.plist" + } +} diff --git a/chromium/ui/base/OWNERS b/chromium/ui/base/OWNERS index b444384491e..b6ad79f101c 100644 --- a/chromium/ui/base/OWNERS +++ b/chromium/ui/base/OWNERS @@ -2,6 +2,8 @@ per-file *.isolate=maruel@chromium.org per-file *.isolate=tandrii@chromium.org per-file *.isolate=vadimsh@chromium.org +per-file template_expressions*=dschuyler@chromium.org + # If you're doing structural changes get a review from one of the ui/OWNERS. per-file *.gyp*=* per-file BUILD.gn=* diff --git a/chromium/ui/base/clipboard/clipboard_android.cc b/chromium/ui/base/clipboard/clipboard_android.cc index 47465b04813..86d86b5be1d 100644 --- a/chromium/ui/base/clipboard/clipboard_android.cc +++ b/chromium/ui/base/clipboard/clipboard_android.cc @@ -116,10 +116,9 @@ void ClipboardMap::CommitToAndroidClipboard() { ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, map_[kPlainTextFormat].c_str()); DCHECK(str.obj()); - Java_Clipboard_setText(env, clipboard_manager_.obj(), str.obj()); } else { - Java_Clipboard_setText(env, clipboard_manager_.obj(), nullptr); + Java_Clipboard_clear(env, clipboard_manager_.obj()); NOTIMPLEMENTED(); } } @@ -128,7 +127,7 @@ void ClipboardMap::Clear() { JNIEnv* env = AttachCurrentThread(); base::AutoLock lock(lock_); map_.clear(); - Java_Clipboard_setText(env, clipboard_manager_.obj(), NULL); + Java_Clipboard_clear(env, clipboard_manager_.obj()); } // Add a key:jstr pair to map, but only if jstr is not null, and also diff --git a/chromium/ui/base/clipboard/clipboard_mac.mm b/chromium/ui/base/clipboard/clipboard_mac.mm index eb4fc21b11a..f3a4c434704 100644 --- a/chromium/ui/base/clipboard/clipboard_mac.mm +++ b/chromium/ui/base/clipboard/clipboard_mac.mm @@ -20,6 +20,7 @@ #include "skia/ext/skia_utils_mac.h" #import "third_party/mozilla/NSPasteboard+Utils.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/clipboard/clipboard_util_mac.h" #include "ui/base/clipboard/custom_data_helper.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/size.h" @@ -29,9 +30,6 @@ namespace ui { namespace { -// Would be nice if this were in UTCoreTypes.h, but it isn't -NSString* const kUTTypeURLName = @"public.url-name"; - // Tells us if WebKit was the last to write to the pasteboard. There's no // actual data associated with this type. NSString* const kWebSmartPastePboardType = @"NeXT smart paste pasteboard type"; @@ -114,7 +112,7 @@ const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { // static const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSStringPboardType)); + CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSPasteboardTypeString)); return type; } @@ -245,7 +243,7 @@ void ClipboardMac::ReadText(ClipboardType type, base::string16* result) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); NSPasteboard* pb = GetPasteboard(); - NSString* contents = [pb stringForType:NSStringPboardType]; + NSString* contents = [pb stringForType:NSPasteboardTypeString]; *result = base::SysNSStringToUTF16(contents); } @@ -255,7 +253,7 @@ void ClipboardMac::ReadAsciiText(ClipboardType type, DCHECK(CalledOnValidThread()); DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); NSPasteboard* pb = GetPasteboard(); - NSString* contents = [pb stringForType:NSStringPboardType]; + NSString* contents = [pb stringForType:NSPasteboardTypeString]; if (!contents) result->clear(); @@ -279,7 +277,7 @@ void ClipboardMac::ReadHTML(ClipboardType type, NSPasteboard* pb = GetPasteboard(); NSArray* supportedTypes = [NSArray arrayWithObjects:NSHTMLPboardType, NSRTFPboardType, - NSStringPboardType, + NSPasteboardTypeString, nil]; NSString* bestType = [pb availableTypeFromArray:supportedTypes]; if (bestType) { @@ -354,12 +352,12 @@ void ClipboardMac::ReadBookmark(base::string16* title, std::string* url) const { NSPasteboard* pb = GetPasteboard(); if (title) { - NSString* contents = [pb stringForType:kUTTypeURLName]; + NSString* contents = ClipboardUtil::GetTitleFromPasteboardURL(pb); *title = base::SysNSStringToUTF16(contents); } if (url) { - NSString* url_string = [[NSURL URLFromPasteboard:pb] absoluteString]; + NSString* url_string = ClipboardUtil::GetURLFromPasteboardURL(pb); if (!url_string) url->clear(); else @@ -393,8 +391,8 @@ void ClipboardMac::WriteText(const char* text_data, size_t text_len) { std::string text_str(text_data, text_len); NSString* text = base::SysUTF8ToNSString(text_str); NSPasteboard* pb = GetPasteboard(); - [pb addTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - [pb setString:text forType:NSStringPboardType]; + [pb addTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; + [pb setString:text forType:NSPasteboardTypeString]; } void ClipboardMac::WriteHTML(const char* markup_data, @@ -425,18 +423,10 @@ void ClipboardMac::WriteBookmark(const char* title_data, std::string url_str(url_data, url_len); NSString* url = base::SysUTF8ToNSString(url_str); - // TODO(playmobil): In the Windows version of this function, an HTML - // representation of the bookmark is also added to the clipboard, to support - // drag and drop of web shortcuts. I don't think we need to do this on the - // Mac, but we should double check later on. - NSURL* nsurl = [NSURL URLWithString:url]; - + base::scoped_nsobject<NSPasteboardItem> item( + ClipboardUtil::PasteboardItemFromUrl(url, title)); NSPasteboard* pb = GetPasteboard(); - // passing UTIs into the pasteboard methods is valid >= 10.5 - [pb addTypes:[NSArray arrayWithObjects:NSURLPboardType, kUTTypeURLName, nil] - owner:nil]; - [nsurl writeToPasteboard:pb]; - [pb setString:title forType:kUTTypeURLName]; + ui::ClipboardUtil::AddDataToPasteboard(pb, item); } void ClipboardMac::WriteBitmap(const SkBitmap& bitmap) { diff --git a/chromium/ui/base/clipboard/clipboard_util_mac.h b/chromium/ui/base/clipboard/clipboard_util_mac.h new file mode 100644 index 00000000000..1c4ff0ad99d --- /dev/null +++ b/chromium/ui/base/clipboard/clipboard_util_mac.h @@ -0,0 +1,70 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_CLIPBOARD_CLIPBOARD_UTIL_MAC_H_ +#define UI_BASE_CLIPBOARD_CLIPBOARD_UTIL_MAC_H_ + +#import <AppKit/AppKit.h> + +#include "base/mac/scoped_nsobject.h" +#include "base/memory/ref_counted.h" +#include "ui/base/ui_base_export.h" + +namespace ui { + +class UI_BASE_EXPORT UniquePasteboard + : public base::RefCounted<UniquePasteboard> { + public: + UniquePasteboard(); + + NSPasteboard* get() { return pasteboard_; } + + private: + friend class base::RefCounted<UniquePasteboard>; + ~UniquePasteboard(); + base::scoped_nsobject<NSPasteboard> pasteboard_; +}; + +class UI_BASE_EXPORT ClipboardUtil { + public: + // Returns an NSPasteboardItem that represents the given |url|. + // |url| must not be nil. + // If |title| is nil, |url| is used in its place. + static base::scoped_nsobject<NSPasteboardItem> PasteboardItemFromUrl( + NSString* url, + NSString* title); + + // Returns an NSPasteboardItem that represents the given |urls| and |titles|. + static base::scoped_nsobject<NSPasteboardItem> PasteboardItemFromUrls( + NSArray* urls, + NSArray* titles); + + // Returns an NSPasteboardItem that represents the given string. + // |string| must not be nil. + static base::scoped_nsobject<NSPasteboardItem> PasteboardItemFromString( + NSString* string); + + // Returns the title or url associated with a NSPasteboard which contains an + // url NSPasteboardItem. + static NSString* GetTitleFromPasteboardURL(NSPasteboard* pboard); + static NSString* GetURLFromPasteboardURL(NSPasteboard* pboard); + + // Returns the UTI of a pasteboard type. + static NSString* UTIForPasteboardType(NSString* type); + static NSString* UTIForWebURLsAndTitles(); + + // For each pasteboard type in |item| that is not in |pboard|, add the type + // and its associated data. + static void AddDataToPasteboard(NSPasteboard* pboard, NSPasteboardItem* item); + + // Returns whether the operation was succesful. On success, the two arrays are + // guaranteed to be equal length, and are populated with strings of |urls| and + // |titles|. + static bool URLsAndTitlesFromPasteboard(NSPasteboard* pboard, + NSArray** urls, + NSArray** titles); +}; +} + +#endif // UI_BASE_CLIPBOARD_CLIPBOARD_UTIL_MAC_H_ diff --git a/chromium/ui/base/clipboard/clipboard_util_mac.mm b/chromium/ui/base/clipboard/clipboard_util_mac.mm new file mode 100644 index 00000000000..7d0d774122c --- /dev/null +++ b/chromium/ui/base/clipboard/clipboard_util_mac.mm @@ -0,0 +1,179 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/clipboard/clipboard_util_mac.h" + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" + +namespace { +NSString* const kWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType"; +NSString* const kCorePasteboardFlavorType_url = + @"CorePasteboardFlavorType 0x75726C20"; // 'url ' url +NSString* const kCorePasteboardFlavorType_urln = + @"CorePasteboardFlavorType 0x75726C6E"; // 'urln' title + +// It's much more convenient to return an NSString than a +// base::ScopedCFTypeRef<CFStringRef>, since the methods on NSPasteboardItem +// require an NSString*. +NSString* UTIFromPboardType(NSString* type) { + return [base::mac::CFToNSCast(UTTypeCreatePreferredIdentifierForTag( + kUTTagClassNSPboardType, base::mac::NSToCFCast(type), kUTTypeData)) + autorelease]; +} +} // namespace + +namespace ui { + +UniquePasteboard::UniquePasteboard() + : pasteboard_([[NSPasteboard pasteboardWithUniqueName] retain]) {} + +UniquePasteboard::~UniquePasteboard() { + [pasteboard_ releaseGlobally]; +} + +// static +base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromUrl( + NSString* urlString, + NSString* title) { + DCHECK(urlString); + if (!title) + title = urlString; + + base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]); + + NSURL* url = [NSURL URLWithString:urlString]; + if ([url isFileURL] && + [[NSFileManager defaultManager] fileExistsAtPath:[url path]]) { + [item setPropertyList:@[ [url path] ] + forType:UTIFromPboardType(NSFilenamesPboardType)]; + } + + // Set Safari's URL + title arrays Pboard type. + NSArray* urlsAndTitles = @[ @[ urlString ], @[ title ] ]; + [item setPropertyList:urlsAndTitles + forType:UTIFromPboardType(kWebURLsWithTitlesPboardType)]; + + // Set NSURLPboardType. The format of the property list is divined from + // Webkit's function PlatformPasteboard::setStringForType. + // https://github.com/WebKit/webkit/blob/master/Source/WebCore/platform/mac/PlatformPasteboardMac.mm + NSURL* base = [url baseURL]; + if (base) { + [item setPropertyList:@[ [url relativeString], [base absoluteString] ] + forType:UTIFromPboardType(NSURLPboardType)]; + } else if (url) { + [item setPropertyList:@[ [url absoluteString], @"" ] + forType:UTIFromPboardType(NSURLPboardType)]; + } + + [item setString:urlString forType:NSPasteboardTypeString]; + + [item setData:[urlString dataUsingEncoding:NSUTF8StringEncoding] + forType:UTIFromPboardType(kCorePasteboardFlavorType_url)]; + + [item setData:[title dataUsingEncoding:NSUTF8StringEncoding] + forType:UTIFromPboardType(kCorePasteboardFlavorType_urln)]; + return item; +} + +// static +base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromUrls( + NSArray* urls, + NSArray* titles) { + base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]); + + // Set Safari's URL + title arrays Pboard type. + NSArray* urlsAndTitles = @[ urls, titles ]; + [item setPropertyList:urlsAndTitles + forType:UTIFromPboardType(kWebURLsWithTitlesPboardType)]; + + return item; +} + +// static +base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromString( + NSString* string) { + base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]); + [item setString:string forType:NSPasteboardTypeString]; + return item; +} + +//static +NSString* ClipboardUtil::GetTitleFromPasteboardURL(NSPasteboard* pboard) { + return + [pboard stringForType:UTIFromPboardType(kCorePasteboardFlavorType_urln)]; +} + +//static +NSString* ClipboardUtil::GetURLFromPasteboardURL(NSPasteboard* pboard) { + return + [pboard stringForType:UTIFromPboardType(kCorePasteboardFlavorType_url)]; +} + +// static +NSString* ClipboardUtil::UTIForPasteboardType(NSString* type) { + return UTIFromPboardType(type); +} + +// static +NSString* ClipboardUtil::UTIForWebURLsAndTitles() { + return UTIFromPboardType(kWebURLsWithTitlesPboardType); +} + +// static +void ClipboardUtil::AddDataToPasteboard(NSPasteboard* pboard, + NSPasteboardItem* item) { + NSSet* oldTypes = [NSSet setWithArray:[pboard types]]; + NSMutableSet* newTypes = [NSMutableSet setWithArray:[item types]]; + [newTypes minusSet:oldTypes]; + + [pboard addTypes:[newTypes allObjects] owner:nil]; + for (NSString* type in newTypes) { + // Technically, the object associated with |type| might be an NSString or a + // property list. It doesn't matter though, since the type gets pulled from + // and shoved into an NSDictionary. + [pboard setData:[item dataForType:type] forType:type]; + } +} + +// static +bool ClipboardUtil::URLsAndTitlesFromPasteboard(NSPasteboard* pboard, + NSArray** urls, + NSArray** titles) { + NSArray* bookmarkPairs = base::mac::ObjCCast<NSArray>([pboard + propertyListForType:UTIFromPboardType(kWebURLsWithTitlesPboardType)]); + if (!bookmarkPairs) + return false; + + if ([bookmarkPairs count] != 2) + return false; + + NSArray* urlsArr = + base::mac::ObjCCast<NSArray>([bookmarkPairs objectAtIndex:0]); + NSArray* titlesArr = + base::mac::ObjCCast<NSArray>([bookmarkPairs objectAtIndex:1]); + + if (!urlsArr || !titlesArr) + return false; + if ([urlsArr count] < 1) + return false; + if ([urlsArr count] != [titlesArr count]) + return false; + + for (id obj in urlsArr) { + if (![obj isKindOfClass:[NSString class]]) + return false; + } + + for (id obj in titlesArr) { + if (![obj isKindOfClass:[NSString class]]) + return false; + } + + *urls = urlsArr; + *titles = titlesArr; + return true; +} + +} // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm b/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm new file mode 100644 index 00000000000..28728c2e9bc --- /dev/null +++ b/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm @@ -0,0 +1,99 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ui/base/clipboard/clipboard_util_mac.h" + +#include "base/mac/scoped_nsobject.h" +#include "base/memory/ref_counted.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" +#include "testing/platform_test.h" +#include "third_party/mozilla/NSPasteboard+Utils.h" + +namespace { + +class ClipboardUtilMacTest : public PlatformTest { + public: + ClipboardUtilMacTest() { } +}; + +TEST_F(ClipboardUtilMacTest, PasteboardItemFromUrl) { + NSString* urlString = + @"https://www.google.com/" + @"search?q=test&oq=test&aqs=chrome..69i57l2j69i60l4.278j0j7&" + @"sourceid=chrome&ie=UTF-8"; + + base::scoped_nsobject<NSPasteboardItem> item( + ui::ClipboardUtil::PasteboardItemFromUrl(urlString, nil)); + scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard; + [pasteboard->get() writeObjects:@[ item ]]; + + NSArray* urls = nil; + NSArray* titles = nil; + [pasteboard->get() getURLs:&urls andTitles:&titles convertingFilenames:NO]; + + ASSERT_EQ(1u, [urls count]); + EXPECT_NSEQ(urlString, [urls objectAtIndex:0]); + ASSERT_EQ(1u, [titles count]); + EXPECT_NSEQ(urlString, [titles objectAtIndex:0]); + + NSURL* url = [NSURL URLFromPasteboard:pasteboard->get()]; + EXPECT_NSEQ([url absoluteString], urlString); +} + +TEST_F(ClipboardUtilMacTest, PasteboardItemWithTitle) { + NSString* urlString = @"https://www.google.com/"; + NSString* title = @"Burrowing Yams"; + + base::scoped_nsobject<NSPasteboardItem> item( + ui::ClipboardUtil::PasteboardItemFromUrl(urlString, title)); + scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard; + [pasteboard->get() writeObjects:@[ item ]]; + + NSArray* urls = nil; + NSArray* titles = nil; + [pasteboard->get() getURLs:&urls andTitles:&titles convertingFilenames:NO]; + + ASSERT_EQ(1u, [urls count]); + EXPECT_NSEQ(urlString, [urls objectAtIndex:0]); + ASSERT_EQ(1u, [titles count]); + EXPECT_NSEQ(title, [titles objectAtIndex:0]); + + NSURL* url = [NSURL URLFromPasteboard:pasteboard->get()]; + EXPECT_NSEQ([url absoluteString], urlString); +} + +TEST_F(ClipboardUtilMacTest, PasteboardItemWithFilePath) { + NSURL* url = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; + ASSERT_TRUE(url); + NSString* urlString = [url absoluteString]; + + base::scoped_nsobject<NSPasteboardItem> item( + ui::ClipboardUtil::PasteboardItemFromUrl(urlString, nil)); + scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard; + [pasteboard->get() writeObjects:@[ item ]]; + + NSArray* urls = nil; + NSArray* titles = nil; + [pasteboard->get() getURLs:&urls andTitles:&titles convertingFilenames:NO]; + + ASSERT_EQ(1u, [urls count]); + EXPECT_NSEQ(urlString, [urls objectAtIndex:0]); + ASSERT_EQ(1u, [titles count]); + EXPECT_NSEQ(urlString, [titles objectAtIndex:0]); + + NSURL* urlFromPasteboard = [NSURL URLFromPasteboard:pasteboard->get()]; + EXPECT_NSEQ(urlFromPasteboard, url); +} + +TEST_F(ClipboardUtilMacTest, CheckForLeak) { + for (int i = 0; i < 10000; ++i) { + @autoreleasepool { + scoped_refptr<ui::UniquePasteboard> pboard = new ui::UniquePasteboard; + EXPECT_TRUE(pboard->get()); + } + } +} + +} // namespace diff --git a/chromium/ui/base/clipboard/custom_data_helper.cc b/chromium/ui/base/clipboard/custom_data_helper.cc index a50a0a072c7..e67829a3107 100644 --- a/chromium/ui/base/clipboard/custom_data_helper.cc +++ b/chromium/ui/base/clipboard/custom_data_helper.cc @@ -43,8 +43,8 @@ void ReadCustomDataTypes(const void* data, SkippablePickle pickle(data, data_length); base::PickleIterator iter(pickle); - size_t size = 0; - if (!iter.ReadSizeT(&size)) + uint32_t size = 0; + if (!iter.ReadUInt32(&size)) return; // Keep track of the original elements in the types vector. On failure, we @@ -52,7 +52,7 @@ void ReadCustomDataTypes(const void* data, // custom data pickles. size_t original_size = types->size(); - for (size_t i = 0; i < size; ++i) { + for (uint32_t i = 0; i < size; ++i) { types->push_back(base::string16()); if (!iter.ReadString16(&types->back()) || !pickle.SkipString16(&iter)) { types->resize(original_size); @@ -68,11 +68,11 @@ void ReadCustomDataForType(const void* data, SkippablePickle pickle(data, data_length); base::PickleIterator iter(pickle); - size_t size = 0; - if (!iter.ReadSizeT(&size)) + uint32_t size = 0; + if (!iter.ReadUInt32(&size)) return; - for (size_t i = 0; i < size; ++i) { + for (uint32_t i = 0; i < size; ++i) { base::string16 deserialized_type; if (!iter.ReadString16(&deserialized_type)) return; @@ -91,11 +91,11 @@ void ReadCustomDataIntoMap(const void* data, base::Pickle pickle(reinterpret_cast<const char*>(data), data_length); base::PickleIterator iter(pickle); - size_t size = 0; - if (!iter.ReadSizeT(&size)) + uint32_t size = 0; + if (!iter.ReadUInt32(&size)) return; - for (size_t i = 0; i < size; ++i) { + for (uint32_t i = 0; i < size; ++i) { base::string16 type; if (!iter.ReadString16(&type)) { // Data is corrupt, return an empty map. @@ -115,7 +115,7 @@ void ReadCustomDataIntoMap(const void* data, void WriteCustomDataToPickle( const std::map<base::string16, base::string16>& data, base::Pickle* pickle) { - pickle->WriteSizeT(data.size()); + pickle->WriteUInt32(data.size()); for (std::map<base::string16, base::string16>::const_iterator it = data.begin(); it != data.end(); diff --git a/chromium/ui/base/clipboard/custom_data_helper_unittest.cc b/chromium/ui/base/clipboard/custom_data_helper_unittest.cc index 1ed53c66750..4d515476559 100644 --- a/chromium/ui/base/clipboard/custom_data_helper_unittest.cc +++ b/chromium/ui/base/clipboard/custom_data_helper_unittest.cc @@ -120,7 +120,7 @@ TEST(CustomDataHelperTest, BadReadTypes) { expected.push_back(ASCIIToUTF16("f")); base::Pickle malformed; - malformed.WriteSizeT(1000); + malformed.WriteUInt32(1000); malformed.WriteString16(ASCIIToUTF16("hello")); malformed.WriteString16(ASCIIToUTF16("world")); std::vector<base::string16> actual(expected); @@ -128,7 +128,7 @@ TEST(CustomDataHelperTest, BadReadTypes) { EXPECT_EQ(expected, actual); base::Pickle malformed2; - malformed2.WriteSizeT(1); + malformed2.WriteUInt32(1); malformed2.WriteString16(ASCIIToUTF16("hello")); std::vector<base::string16> actual2(expected); ReadCustomDataTypes(malformed2.data(), malformed2.size(), &actual2); @@ -140,7 +140,7 @@ TEST(CustomDataHelperTest, BadPickle) { std::map<base::string16, base::string16> result_map; base::Pickle malformed; - malformed.WriteSizeT(1000); + malformed.WriteUInt32(1000); malformed.WriteString16(ASCIIToUTF16("hello")); malformed.WriteString16(ASCIIToUTF16("world")); @@ -153,7 +153,7 @@ TEST(CustomDataHelperTest, BadPickle) { EXPECT_EQ(0u, result_map.size()); base::Pickle malformed2; - malformed2.WriteSizeT(1); + malformed2.WriteUInt32(1); malformed2.WriteString16(ASCIIToUTF16("hello")); ReadCustomDataForType(malformed2.data(), diff --git a/chromium/ui/base/cocoa/appkit_utils.mm b/chromium/ui/base/cocoa/appkit_utils.mm index 6aa90ae6789..ce672699f5d 100644 --- a/chromium/ui/base/cocoa/appkit_utils.mm +++ b/chromium/ui/base/cocoa/appkit_utils.mm @@ -15,18 +15,45 @@ NSImage* GetImage(int image_id) { .ToNSImage(); } -// Whether windows should miniaturize on a double-click on the title bar. -bool ShouldWindowsMiniaturizeOnDoubleClick() { - // We use an undocumented method in Cocoa; if it doesn't exist, default to - // |true|. If it ever goes away, we can do (using an undocumented pref key): - // NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - // return ![defaults objectForKey:@"AppleMiniaturizeOnDoubleClick"] || - // [defaults boolForKey:@"AppleMiniaturizeOnDoubleClick"]; +// Double-click in window title bar actions. +enum class DoubleClickAction { + NONE, + MINIMIZE, + MAXIMIZE, +}; + +// The action to take when the user double-clicks in the window title bar. +DoubleClickAction WindowTitleBarDoubleClickAction() { + // El Capitan introduced a Dock preference to configure the window title bar + // double-click action (Minimize, Maximize, or nothing). + if (base::mac::IsOSElCapitanOrLater()) { + NSString* doubleClickAction = [[NSUserDefaults standardUserDefaults] + objectForKey:@"AppleActionOnDoubleClick"]; + + if ([doubleClickAction isEqualToString:@"Minimize"]) { + return DoubleClickAction::MINIMIZE; + } else if ([doubleClickAction isEqualToString:@"Maximize"]) { + return DoubleClickAction::MAXIMIZE; + } + + return DoubleClickAction::NONE; + } + + // Determine minimize using an undocumented method in Cocoa. If we're + // running on an earlier version of the OS that doesn't implement it, + // just default to the minimize action. BOOL methodImplemented = [NSWindow respondsToSelector:@selector(_shouldMiniaturizeOnDoubleClick)]; - DCHECK(methodImplemented); - return !methodImplemented || - [NSWindow performSelector:@selector(_shouldMiniaturizeOnDoubleClick)]; + if (!methodImplemented || + [NSWindow performSelector:@selector(_shouldMiniaturizeOnDoubleClick)]) { + return DoubleClickAction::MINIMIZE; + } + + // At this point _shouldMiniaturizeOnDoubleClick has returned |NO|. On + // Yosemite, that means a double-click should Maximize the window, and on + // all prior OSes a double-click should do nothing. + return base::mac::IsOSYosemite() ? DoubleClickAction::MAXIMIZE + : DoubleClickAction::NONE; } } // namespace @@ -54,10 +81,17 @@ void DrawNinePartImage(NSRect frame, } void WindowTitlebarReceivedDoubleClick(NSWindow* window, id sender) { - if (ShouldWindowsMiniaturizeOnDoubleClick()) { - [window performMiniaturize:sender]; - } else if (base::mac::IsOSYosemiteOrLater()) { - [window performZoom:sender]; + switch (WindowTitleBarDoubleClickAction()) { + case DoubleClickAction::MINIMIZE: + [window performMiniaturize:sender]; + break; + + case DoubleClickAction::MAXIMIZE: + [window performZoom:sender]; + break; + + case DoubleClickAction::NONE: + break; } } diff --git a/chromium/ui/base/cocoa/cocoa_base_utils.h b/chromium/ui/base/cocoa/cocoa_base_utils.h index c479f72f2a5..627fe3ba325 100644 --- a/chromium/ui/base/cocoa/cocoa_base_utils.h +++ b/chromium/ui/base/cocoa/cocoa_base_utils.h @@ -26,6 +26,14 @@ UI_BASE_EXPORT WindowOpenDisposition UI_BASE_EXPORT WindowOpenDisposition WindowOpenDispositionFromNSEventWithFlags(NSEvent* event, NSUInteger flags); +// Converts a point from window coordinates to screen coordinates. +UI_BASE_EXPORT NSPoint ConvertPointFromWindowToScreen(NSWindow* window, + NSPoint point); + +// Converts a point from screen coordinates to window coordinates. +UI_BASE_EXPORT NSPoint ConvertPointFromScreenToWindow(NSWindow* window, + NSPoint point); + } // namespace ui #endif // UI_BASE_COCOA_COCOA_BASE_UTILS_H_ diff --git a/chromium/ui/base/cocoa/cocoa_base_utils.mm b/chromium/ui/base/cocoa/cocoa_base_utils.mm index 0830f3488a8..ca5d885abf8 100644 --- a/chromium/ui/base/cocoa/cocoa_base_utils.mm +++ b/chromium/ui/base/cocoa/cocoa_base_utils.mm @@ -4,6 +4,7 @@ #include "ui/base/cocoa/cocoa_base_utils.h" +#include "base/mac/sdk_forward_declarations.h" #include "ui/events/cocoa/cocoa_event_utils.h" namespace ui { @@ -19,4 +20,14 @@ WindowOpenDisposition WindowOpenDispositionFromNSEventWithFlags( return DispositionFromEventFlags(event_flags); } +NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { + NSRect point_rect = NSMakeRect(point.x, point.y, 0, 0); + return [window convertRectToScreen:point_rect].origin; +} + +NSPoint ConvertPointFromScreenToWindow(NSWindow* window, NSPoint point) { + NSRect point_rect = NSMakeRect(point.x, point.y, 0, 0); + return [window convertRectFromScreen:point_rect].origin; +} + } // namespace ui diff --git a/chromium/ui/base/cocoa/command_dispatcher.mm b/chromium/ui/base/cocoa/command_dispatcher.mm index 64ac8fd1bb2..46a351b4ce8 100644 --- a/chromium/ui/base/cocoa/command_dispatcher.mm +++ b/chromium/ui/base/cocoa/command_dispatcher.mm @@ -5,6 +5,7 @@ #import "ui/base/cocoa/command_dispatcher.h" #include "base/logging.h" +#include "ui/base/cocoa/cocoa_base_utils.h" namespace { @@ -15,8 +16,8 @@ NSEvent* KeyEventForWindow(NSWindow* window, NSEvent* event) { // Convert the event's location from the original window's coordinates into // our own. NSPoint location = [event locationInWindow]; - location = [[event window] convertBaseToScreen:location]; - location = [window convertScreenToBase:location]; + location = ui::ConvertPointFromWindowToScreen([event window], location); + location = ui::ConvertPointFromScreenToWindow(window, location); // Various things *only* apply to key down/up. bool is_a_repeat = false; diff --git a/chromium/ui/base/cocoa/controls/blue_label_button.mm b/chromium/ui/base/cocoa/controls/blue_label_button.mm index 9d9b5805c9c..37ecc8da0f2 100644 --- a/chromium/ui/base/cocoa/controls/blue_label_button.mm +++ b/chromium/ui/base/cocoa/controls/blue_label_button.mm @@ -29,6 +29,8 @@ const SkColor kPressInnerRingColor = SkColorSetRGB(0x3f, 0x73, 0xcd); const SkColor kOuterRingColor = SkColorSetRGB(0x2b, 0x67, 0xce); const SkColor kPressOuterRingColor = SkColorSetRGB(0x23, 0x52, 0xa2); +const int kFontSizeDelta = ui::ResourceBundle::kSmallFontDelta; + @interface BlueLabelButtonCell : NSButtonCell + (NSAttributedString*)generateAttributedString:(NSString*)buttonText; @@ -54,8 +56,7 @@ const SkColor kPressOuterRingColor = SkColorSetRGB(0x23, 0x52, 0xa2); + (NSAttributedString*)generateAttributedString:(NSString*)buttonText { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - NSFont* buttonFont = rb.GetFontList(ui::ResourceBundle::SmallFont). - GetPrimaryFont().GetNativeFont(); + NSFont* buttonFont = rb.GetFontWithDelta(kFontSizeDelta).GetNativeFont(); base::scoped_nsobject<NSMutableParagraphStyle> buttonTextParagraphStyle( [[NSMutableParagraphStyle alloc] init]); [buttonTextParagraphStyle setAlignment:NSCenterTextAlignment]; diff --git a/chromium/ui/base/cocoa/controls/hyperlink_button_cell.mm b/chromium/ui/base/cocoa/controls/hyperlink_button_cell.mm index e67e60db12e..754be72f613 100644 --- a/chromium/ui/base/cocoa/controls/hyperlink_button_cell.mm +++ b/chromium/ui/base/cocoa/controls/hyperlink_button_cell.mm @@ -91,7 +91,7 @@ using hyperlink_button_cell::UnderlineBehavior; // Creates the NSDictionary of attributes for the attributed string. - (NSDictionary*)linkAttributes { - NSUInteger underlineMask = NSNoUnderlineStyle; + NSUInteger underlineMask = NSUnderlineStyleNone; if (underlineBehavior_ == UnderlineBehavior::ALWAYS || (mouseIsInside_ && [self isEnabled] && underlineBehavior_ == UnderlineBehavior::ON_HOVER)) { diff --git a/chromium/ui/base/cocoa/controls/hyperlink_text_view.mm b/chromium/ui/base/cocoa/controls/hyperlink_text_view.mm index 2a50e0f4a94..dd5e4504333 100644 --- a/chromium/ui/base/cocoa/controls/hyperlink_text_view.mm +++ b/chromium/ui/base/cocoa/controls/hyperlink_text_view.mm @@ -162,7 +162,7 @@ const float kTextBaselineShift = -1.0; NSUnderlineStyleAttributeName : @(YES), NSCursorAttributeName : [NSCursor pointingHandCursor], NSLinkAttributeName : url, - NSUnderlineStyleAttributeName : @(NSSingleUnderlineStyle) + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) }; [[self textStorage] addAttributes:attributes range:range]; diff --git a/chromium/ui/base/cocoa/controls/hyperlink_text_view_unittest.mm b/chromium/ui/base/cocoa/controls/hyperlink_text_view_unittest.mm index 671be65386c..28014246f75 100644 --- a/chromium/ui/base/cocoa/controls/hyperlink_text_view_unittest.mm +++ b/chromium/ui/base/cocoa/controls/hyperlink_text_view_unittest.mm @@ -40,11 +40,12 @@ class HyperlinkTextViewTest : public ui::CocoaTest { [[NSMutableDictionary dictionaryWithDictionary: GetDefaultTextAttributes()] retain]); [linkAttributes_ addEntriesFromDictionary:@{ - NSForegroundColorAttributeName : [NSColor blueColor], - NSUnderlineStyleAttributeName : @(YES), - NSCursorAttributeName : [NSCursor pointingHandCursor], - NSUnderlineStyleAttributeName : @(NSSingleUnderlineStyle), - NSLinkAttributeName : @""}]; + NSForegroundColorAttributeName : [NSColor blueColor], + NSUnderlineStyleAttributeName : @(YES), + NSCursorAttributeName : [NSCursor pointingHandCursor], + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle), + NSLinkAttributeName : @"" + }]; } return [NSMutableDictionary dictionaryWithDictionary:linkAttributes_]; } diff --git a/chromium/ui/base/cocoa/menu_controller_unittest.mm b/chromium/ui/base/cocoa/menu_controller_unittest.mm index 5bda6b6c096..57a32bed3fb 100644 --- a/chromium/ui/base/cocoa/menu_controller_unittest.mm +++ b/chromium/ui/base/cocoa/menu_controller_unittest.mm @@ -271,8 +271,9 @@ TEST_F(MenuControllerTest, Validate) { // Tests that items which have a font set actually use that font. TEST_F(MenuControllerTest, LabelFontList) { Delegate delegate; - const gfx::FontList& bold = ResourceBundle::GetSharedInstance().GetFontList( - ResourceBundle::BoldFont); + const gfx::FontList& bold = + ResourceBundle::GetSharedInstance().GetFontListWithDelta(0, + gfx::Font::BOLD); FontListMenuModel model(&delegate, &bold, 0); model.AddItem(1, ASCIIToUTF16("one")); model.AddItem(2, ASCIIToUTF16("two")); diff --git a/chromium/ui/base/cocoa/nib_loading.mm b/chromium/ui/base/cocoa/nib_loading.mm index dd0eb48d806..6deea19f698 100644 --- a/chromium/ui/base/cocoa/nib_loading.mm +++ b/chromium/ui/base/cocoa/nib_loading.mm @@ -7,6 +7,11 @@ #include "base/mac/bundle_locations.h" #include "base/mac/scoped_nsobject.h" +@interface NSNib (MountainLionSDK) +- (BOOL)instantiateWithOwner:(nullable id)owner + topLevelObjects:(NSArray* __nonnull* __nullable)topLevelObjects; +@end + namespace ui { NSView* GetViewFromNib(NSString* name) { @@ -17,18 +22,10 @@ NSView* GetViewFromNib(NSString* name) { return nil; NSArray* objects; - BOOL success = [nib instantiateNibWithOwner:nil - topLevelObjects:&objects]; + BOOL success = [nib instantiateWithOwner:nil topLevelObjects:&objects]; if (!success) return nil; - // When loading a nib manually (as opposed to using an NSWindowController or - // NSViewController), all the top-level objects need to be explicitly - // released. See - // http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW10 - // for more information. - [objects makeObjectsPerformSelector:@selector(release)]; - // For some strange reason, even nibs that appear to have but one top-level // object often have more (an NSApplication, etc.). Filter out what isn't // desired. diff --git a/chromium/ui/base/cocoa/three_part_image.h b/chromium/ui/base/cocoa/three_part_image.h index ebeef5fdc4d..72da5ca918a 100644 --- a/chromium/ui/base/cocoa/three_part_image.h +++ b/chromium/ui/base/cocoa/three_part_image.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_BASE_COCOA_THREE_PART_IMAGE_H -#define UI_BASE_COCOA_THREE_PART_IMAGE_H +#ifndef UI_BASE_COCOA_THREE_PART_IMAGE_H_ +#define UI_BASE_COCOA_THREE_PART_IMAGE_H_ #import <Cocoa/Cocoa.h> @@ -18,9 +18,9 @@ namespace ui { // Vertical orientation is not currently supported. class UI_BASE_EXPORT ThreePartImage { public: - // |left_id|, |middle_id|, and |right_id| are ResourceBundle image - // identifiers. Specify 0 for |middle_id| if there is no middle image. - ThreePartImage(int left_id, int middle_id, int right_id); + // Create a ThreePartImage from existing NSImages. Specify nil for |middle| if + // there is no middle image. + ThreePartImage(NSImage* left, NSImage* middle, NSImage* right); ~ThreePartImage(); // Returns the image rects if drawn in |bounds|. @@ -51,4 +51,4 @@ class UI_BASE_EXPORT ThreePartImage { } // namespace ui -#endif // UI_BASE_COCOA_THREE_PART_IMAGE_H +#endif // UI_BASE_COCOA_THREE_PART_IMAGE_H_ diff --git a/chromium/ui/base/cocoa/three_part_image.mm b/chromium/ui/base/cocoa/three_part_image.mm index 5fb8916f9f2..18ec2ddbfd0 100644 --- a/chromium/ui/base/cocoa/three_part_image.mm +++ b/chromium/ui/base/cocoa/three_part_image.mm @@ -9,17 +9,17 @@ namespace ui { -ThreePartImage::ThreePartImage(int left_id, int middle_id, int right_id) { - DCHECK(left_id); - DCHECK(right_id); - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - leftImage_.reset(rb.GetNativeImageNamed(left_id).CopyNSImage()); - rightImage_.reset(rb.GetNativeImageNamed(right_id).CopyNSImage()); +ThreePartImage::ThreePartImage(NSImage* left, NSImage* middle, NSImage* right) { + DCHECK(left); + DCHECK(right); + leftImage_.reset([left retain]); + rightImage_.reset([right retain]); leftSize_ = [leftImage_ size]; rightSize_ = [rightImage_ size]; - if (middle_id) - middleImage_.reset(rb.GetNativeImageNamed(middle_id).CopyNSImage()); + if (middle) { + middleImage_.reset([middle retain]); + } } ThreePartImage::~ThreePartImage() { diff --git a/chromium/ui/base/cocoa/three_part_image_unittest.mm b/chromium/ui/base/cocoa/three_part_image_unittest.mm index 43db298ec77..efccbefce07 100644 --- a/chromium/ui/base/cocoa/three_part_image_unittest.mm +++ b/chromium/ui/base/cocoa/three_part_image_unittest.mm @@ -6,6 +6,7 @@ #include "base/memory/scoped_ptr.h" #include "testing/gtest_mac.h" +#include "ui/base/resource/resource_bundle.h" #import "ui/gfx/test/ui_cocoa_test_helper.h" #include "ui/resources/grit/ui_resources.h" @@ -13,9 +14,14 @@ namespace ui { namespace test { TEST(ThreePartImageTest, GetRects) { - ThreePartImage image(IDR_BROWSER_ACTION_BADGE_LEFT, - IDR_BROWSER_ACTION_BADGE_CENTER, - IDR_BROWSER_ACTION_BADGE_RIGHT); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + base::scoped_nsobject<NSImage> leftImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_LEFT).CopyNSImage()); + base::scoped_nsobject<NSImage> middleImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_CENTER).CopyNSImage()); + base::scoped_nsobject<NSImage> rightImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_RIGHT).CopyNSImage()); + ThreePartImage image(leftImage, middleImage, rightImage); NSRect bounds = NSMakeRect(0, 0, 20, 11); EXPECT_NSRECT_EQ(NSMakeRect(0, 0, 4, 11), image.GetLeftRect(bounds)); EXPECT_NSRECT_EQ(NSMakeRect(4, 0, 12, 11), image.GetMiddleRect(bounds)); @@ -23,9 +29,12 @@ TEST(ThreePartImageTest, GetRects) { } TEST(ThreePartImageTest, GetRectsWithoutMiddle) { - ThreePartImage image(IDR_BROWSER_ACTION_BADGE_LEFT, - 0, - IDR_BROWSER_ACTION_BADGE_RIGHT); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + base::scoped_nsobject<NSImage> leftImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_LEFT).CopyNSImage()); + base::scoped_nsobject<NSImage> rightImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_RIGHT).CopyNSImage()); + ThreePartImage image(leftImage, nullptr, rightImage); NSRect bounds = NSMakeRect(0, 0, 20, 11); EXPECT_NSRECT_EQ(NSMakeRect(0, 0, 4, 11), image.GetLeftRect(bounds)); EXPECT_NSRECT_EQ(NSMakeRect(4, 0, 12, 11), image.GetMiddleRect(bounds)); @@ -33,7 +42,12 @@ TEST(ThreePartImageTest, GetRectsWithoutMiddle) { } TEST(ThreePartImageTest, HitTest) { - ThreePartImage image(IDR_BACK_ARROW, 0, IDR_FORWARD_ARROW); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + base::scoped_nsobject<NSImage> leftImage( + rb.GetNativeImageNamed(IDR_BACK_ARROW).CopyNSImage()); + base::scoped_nsobject<NSImage> rightImage( + rb.GetNativeImageNamed(IDR_FORWARD_ARROW).CopyNSImage()); + ThreePartImage image(leftImage, nullptr, rightImage); NSRect bounds = NSMakeRect(0, 0, 512, 128); // The middle of the arrows are hits. diff --git a/chromium/ui/base/cursor/cursors_aura.cc b/chromium/ui/base/cursor/cursors_aura.cc index 8dca8ff1500..993368e39c0 100644 --- a/chromium/ui/base/cursor/cursors_aura.cc +++ b/chromium/ui/base/cursor/cursors_aura.cc @@ -13,6 +13,11 @@ #include "ui/gfx/image/image_skia.h" #include "ui/resources/grit/ui_resources.h" +#if defined(OS_WIN) +#include "ui/base/cursor/cursor_loader_win.h" +#include "ui/gfx/icon_util.h" +#endif + namespace ui { namespace { @@ -228,6 +233,13 @@ bool GetCursorBitmap(const Cursor& cursor, SkBitmap* bitmap, gfx::Point* point) { DCHECK(bitmap && point); +#if defined(OS_WIN) + Cursor cursor_copy = cursor; + ui::CursorLoaderWin cursor_loader; + cursor_loader.SetPlatformCursor(&cursor_copy); + const scoped_ptr<SkBitmap> cursor_bitmap(IconUtil::CreateSkBitmapFromHICON( + cursor_copy.platform())); +#else int resource_id; if (!GetCursorDataFor(ui::CURSOR_SET_NORMAL, cursor.native_type(), @@ -239,6 +251,7 @@ bool GetCursorBitmap(const Cursor& cursor, const SkBitmap* cursor_bitmap = ResourceBundle::GetSharedInstance(). GetImageSkiaNamed(resource_id)->bitmap(); +#endif if (!cursor_bitmap) return false; *bitmap = *cursor_bitmap; diff --git a/chromium/ui/base/default_style.h b/chromium/ui/base/default_style.h new file mode 100644 index 00000000000..49790af2a8e --- /dev/null +++ b/chromium/ui/base/default_style.h @@ -0,0 +1,40 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_DEFAULT_STYLE_H_ +#define UI_BASE_DEFAULT_STYLE_H_ + +#include "build/build_config.h" + +// This file contains the constants that provide the default style for UI +// controls and dialogs. + +namespace ui { + +// Default font size delta for messages in dialogs. Note that on Windows, the +// "base" font size is determined by consulting the system for the font used in +// native MessageBox dialogs. On Mac, it is [NSFont systemFontSize]. Linux +// consults the default font description for a GTK Widget context. On ChromeOS, +// ui::ResourceBundle provides a description via IDS_UI_FONT_FAMILY_CROS. +const int kMessageFontSizeDelta = 0; + +// Default font size delta for dialog buttons, textfields, and labels. +#if defined(OS_MACOSX) +// Cocoa dialogs prefer [NSFont smallSystemFontSize] for labels (typically 11pt +// vs 13pt). +const int kLabelFontSizeDelta = -2; +#else +const int kLabelFontSizeDelta = 0; +#endif + +// Font size delta for dialog titles. +#if defined(OS_MACOSX) +const int kTitleFontSizeDelta = 0; +#else +const int kTitleFontSizeDelta = 3; +#endif + +} // namespace ui + +#endif // UI_BASE_DEFAULT_STYLE_H_ diff --git a/chromium/ui/base/default_theme_provider.h b/chromium/ui/base/default_theme_provider.h index 35d47b2fedb..91ee8d86d11 100644 --- a/chromium/ui/base/default_theme_provider.h +++ b/chromium/ui/base/default_theme_provider.h @@ -35,6 +35,8 @@ class UI_BASE_EXPORT DefaultThemeProvider : public ThemeProvider { #if defined(OS_MACOSX) bool UsingSystemTheme() const override; + bool InIncognitoMode() const override; + bool HasCustomColor(int id) const override; NSImage* GetNSImageNamed(int id) const override; NSColor* GetNSImageColorNamed(int id) const override; NSColor* GetNSColor(int id) const override; diff --git a/chromium/ui/base/default_theme_provider_mac.mm b/chromium/ui/base/default_theme_provider_mac.mm index 71b0f8eb86b..74acb3b4982 100644 --- a/chromium/ui/base/default_theme_provider_mac.mm +++ b/chromium/ui/base/default_theme_provider_mac.mm @@ -14,6 +14,14 @@ bool DefaultThemeProvider::UsingSystemTheme() const { return true; } +bool DefaultThemeProvider::InIncognitoMode() const { + return false; +} + +bool DefaultThemeProvider::HasCustomColor(int id) const { + return false; +} + NSImage* DefaultThemeProvider::GetNSImageNamed(int id) const { return ResourceBundle::GetSharedInstance(). GetNativeImageNamed(id).ToNSImage(); diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc index 9cd070a29e6..009c3eec9b5 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc @@ -6,7 +6,6 @@ #include "base/logging.h" #include "base/strings/utf_string_conversions.h" -#include "net/base/net_util.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/dragdrop/file_info.h" diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11_unittest.cc index 457ba6b41f7..90fa7c20420 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11_unittest.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11_unittest.cc @@ -13,7 +13,7 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/dragdrop/file_info.h" -#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/events/platform/x11/x11_event_source_glib.h" #include "url/gurl.h" const char kFileURL[] = "file:///home/user/file.txt"; @@ -39,7 +39,7 @@ class OSExchangeDataProviderAuraX11Test : public testing::Test { protected: base::MessageLoopForUI message_loop; - X11EventSource event_source; + X11EventSourceGlib event_source; ui::OSExchangeDataProviderAuraX11 provider; }; diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h index c619ab9d806..378602923a9 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h @@ -5,20 +5,21 @@ #ifndef UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_MAC_H_ #define UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_MAC_H_ -#import "base/mac/scoped_nsobject.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" +#import "ui/base/clipboard/clipboard_util_mac.h" #include "ui/base/dragdrop/os_exchange_data.h" @class NSPasteboard; namespace ui { +class UniquePasteboard; // OSExchangeData::Provider implementation for Mac. class UI_BASE_EXPORT OSExchangeDataProviderMac : public OSExchangeData::Provider { public: OSExchangeDataProviderMac(); - explicit OSExchangeDataProviderMac(NSPasteboard* pasteboard); ~OSExchangeDataProviderMac() override; // Overridden from OSExchangeData::Provider: @@ -45,7 +46,8 @@ class UI_BASE_EXPORT OSExchangeDataProviderMac bool HasCustomFormat(const Clipboard::FormatType& format) const override; private: - base::scoped_nsobject<NSPasteboard> pasteboard_; + explicit OSExchangeDataProviderMac(scoped_refptr<ui::UniquePasteboard>); + scoped_refptr<ui::UniquePasteboard> pasteboard_; DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderMac); }; diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm index 72d647f1406..030d0d08412 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm @@ -11,17 +11,17 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #import "third_party/mozilla/NSPasteboard+Utils.h" +#import "ui/base/clipboard/clipboard_util_mac.h" #include "url/gurl.h" namespace ui { OSExchangeDataProviderMac::OSExchangeDataProviderMac() - : pasteboard_([[NSPasteboard pasteboardWithUniqueName] retain]) { -} + : pasteboard_(new ui::UniquePasteboard) {} -OSExchangeDataProviderMac::OSExchangeDataProviderMac(NSPasteboard* pasteboard) - : pasteboard_([pasteboard retain]) { -} +OSExchangeDataProviderMac::OSExchangeDataProviderMac( + scoped_refptr<ui::UniquePasteboard> pb) + : pasteboard_(pb) {} OSExchangeDataProviderMac::~OSExchangeDataProviderMac() { } @@ -40,21 +40,21 @@ bool OSExchangeDataProviderMac::DidOriginateFromRenderer() const { } void OSExchangeDataProviderMac::SetString(const base::string16& string) { - [pasteboard_ writeObjects:@[ base::SysUTF16ToNSString(string) ]]; + [pasteboard_->get() writeObjects:@[ base::SysUTF16ToNSString(string) ]]; } void OSExchangeDataProviderMac::SetURL(const GURL& url, const base::string16& title) { NSURL* ns_url = [NSURL URLWithString:base::SysUTF8ToNSString(url.spec())]; - [pasteboard_ writeObjects:@[ ns_url ]]; + [pasteboard_->get() writeObjects:@[ ns_url ]]; - [pasteboard_ setString:base::SysUTF16ToNSString(title) - forType:kCorePasteboardFlavorType_urln]; + [pasteboard_->get() setString:base::SysUTF16ToNSString(title) + forType:kCorePasteboardFlavorType_urln]; } void OSExchangeDataProviderMac::SetFilename(const base::FilePath& path) { - [pasteboard_ setPropertyList:@[ base::SysUTF8ToNSString(path.value()) ] - forType:NSFilenamesPboardType]; + [pasteboard_->get() setPropertyList:@[ base::SysUTF8ToNSString(path.value()) ] + forType:NSFilenamesPboardType]; } void OSExchangeDataProviderMac::SetFilenames( @@ -66,13 +66,14 @@ void OSExchangeDataProviderMac::SetPickledData( const Clipboard::FormatType& format, const base::Pickle& data) { NSData* ns_data = [NSData dataWithBytes:data.data() length:data.size()]; - [pasteboard_ setData:ns_data forType:format.ToNSString()]; + [pasteboard_->get() setData:ns_data forType:format.ToNSString()]; } bool OSExchangeDataProviderMac::GetString(base::string16* data) const { DCHECK(data); - NSArray* items = [pasteboard_ readObjectsForClasses:@[ [NSString class] ] - options:@{ }]; + NSArray* items = + [pasteboard_->get() readObjectsForClasses:@[ [NSString class] ] + options:@{}]; // There was no NSString, check for an NSURL. if ([items count] == 0) { @@ -95,8 +96,8 @@ bool OSExchangeDataProviderMac::GetURLAndTitle( base::string16* title) const { DCHECK(url); DCHECK(title); - NSArray* items = [pasteboard_ readObjectsForClasses:@[ [NSURL class] ] - options:@{ }]; + NSArray* items = + [pasteboard_->get() readObjectsForClasses:@[ [NSURL class] ] options:@{}]; if ([items count] == 0) return false; @@ -105,7 +106,8 @@ bool OSExchangeDataProviderMac::GetURLAndTitle( if (policy == OSExchangeData::DO_NOT_CONVERT_FILENAMES) { // If the URL matches a filename, assume that it came from SetFilename(). // Don't return it if we are not supposed to convert filename to URL. - NSArray* paths = [pasteboard_ propertyListForType:NSFilenamesPboardType]; + NSArray* paths = + [pasteboard_->get() propertyListForType:NSFilenamesPboardType]; NSString* url_path = [[ns_url path] stringByStandardizingPath]; for (NSString* path in paths) { if ([[path stringByStandardizingPath] isEqualToString:url_path]) @@ -115,12 +117,13 @@ bool OSExchangeDataProviderMac::GetURLAndTitle( *url = GURL([[ns_url absoluteString] UTF8String]); *title = base::SysNSStringToUTF16( - [pasteboard_ stringForType:kCorePasteboardFlavorType_urln]); + [pasteboard_->get() stringForType:kCorePasteboardFlavorType_urln]); return true; } bool OSExchangeDataProviderMac::GetFilename(base::FilePath* path) const { - NSArray* paths = [pasteboard_ propertyListForType:NSFilenamesPboardType]; + NSArray* paths = + [pasteboard_->get() propertyListForType:NSFilenamesPboardType]; if ([paths count] == 0) return false; @@ -138,7 +141,7 @@ bool OSExchangeDataProviderMac::GetPickledData( const Clipboard::FormatType& format, base::Pickle* data) const { DCHECK(data); - NSData* ns_data = [pasteboard_ dataForType:format.ToNSString()]; + NSData* ns_data = [pasteboard_->get() dataForType:format.ToNSString()]; if (!ns_data) return false; @@ -149,7 +152,7 @@ bool OSExchangeDataProviderMac::GetPickledData( bool OSExchangeDataProviderMac::HasString() const { NSArray* classes = @[ [NSString class] ]; - return [pasteboard_ canReadObjectForClasses:classes options:nil]; + return [pasteboard_->get() canReadObjectForClasses:classes options:nil]; } bool OSExchangeDataProviderMac::HasURL( @@ -160,12 +163,12 @@ bool OSExchangeDataProviderMac::HasURL( } bool OSExchangeDataProviderMac::HasFile() const { - return [[pasteboard_ types] containsObject:NSFilenamesPboardType]; + return [[pasteboard_->get() types] containsObject:NSFilenamesPboardType]; } bool OSExchangeDataProviderMac::HasCustomFormat( const Clipboard::FormatType& format) const { - return [[pasteboard_ types] containsObject:format.ToNSString()]; + return [[pasteboard_->get() types] containsObject:format.ToNSString()]; } /////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/base/ime/BUILD.gn b/chromium/ui/base/ime/BUILD.gn index b915757c0d4..668f4f9d0ae 100644 --- a/chromium/ui/base/ime/BUILD.gn +++ b/chromium/ui/base/ime/BUILD.gn @@ -5,6 +5,14 @@ import("//build/config/ui.gni") import("//testing/test.gni") +source_set("text_input_types") { + sources = [ + "text_input_flags.h", + "text_input_mode.h", + "text_input_type.h", + ] +} + component("ime") { output_name = "ui_base_ime" sources = [ @@ -34,8 +42,6 @@ component("ime") { "chromeos/input_method_descriptor.h", "chromeos/input_method_manager.cc", "chromeos/input_method_manager.h", - "chromeos/input_method_whitelist.cc", - "chromeos/input_method_whitelist.h", "chromeos/mock_component_extension_ime_manager_delegate.cc", "chromeos/mock_component_extension_ime_manager_delegate.h", "chromeos/mock_ime_candidate_window_handler.cc", @@ -51,9 +57,7 @@ component("ime") { "composition_underline.h", "ime_bridge.cc", "ime_bridge.h", - "ime_engine_handler_interface.cc", "ime_engine_handler_interface.h", - "ime_engine_observer.h", "ime_input_context_handler_interface.h", "infolist_entry.cc", "infolist_entry.h", @@ -85,12 +89,8 @@ component("ime") { "linux/linux_input_method_context_factory.h", "mock_input_method.cc", "mock_input_method.h", - "remote_input_method_delegate_win.h", - "remote_input_method_win.cc", - "remote_input_method_win.h", "text_input_client.cc", "text_input_client.h", - "text_input_type.h", "ui_base_ime_export.h", "win/imm32_manager.cc", "win/imm32_manager.h", @@ -116,6 +116,10 @@ component("ime") { "//url", ] + public_deps = [ + ":text_input_types", + ] + if (!use_aura || (!is_linux && !use_ozone)) { sources -= [ "input_method_auralinux.cc", @@ -142,7 +146,6 @@ component("ime") { if (is_chromeos) { deps += [ "//chromeos", - "//chromeos/ime:gencode", "//ui/events:dom_keycode_converter", ] if (!use_ozone) { diff --git a/chromium/ui/base/ime/candidate_window.cc b/chromium/ui/base/ime/candidate_window.cc index 60f8c4d7c33..30259f50b05 100644 --- a/chromium/ui/base/ime/candidate_window.cc +++ b/chromium/ui/base/ime/candidate_window.cc @@ -99,6 +99,8 @@ CandidateWindow::CandidateWindowProperty::~CandidateWindowProperty() { CandidateWindow::Entry::Entry() { } +CandidateWindow::Entry::Entry(const Entry& other) = default; + CandidateWindow::Entry::~Entry() { } diff --git a/chromium/ui/base/ime/candidate_window.h b/chromium/ui/base/ime/candidate_window.h index ad52c33e309..ca2cc2be17d 100644 --- a/chromium/ui/base/ime/candidate_window.h +++ b/chromium/ui/base/ime/candidate_window.h @@ -43,6 +43,7 @@ class UI_BASE_IME_EXPORT CandidateWindow { // Represents a candidate entry. struct UI_BASE_IME_EXPORT Entry { Entry(); + Entry(const Entry& other); virtual ~Entry(); base::string16 value; base::string16 label; diff --git a/chromium/ui/base/ime/composition_text.cc b/chromium/ui/base/ime/composition_text.cc index d3ae7f3a399..a2e583f6221 100644 --- a/chromium/ui/base/ime/composition_text.cc +++ b/chromium/ui/base/ime/composition_text.cc @@ -9,6 +9,8 @@ namespace ui { CompositionText::CompositionText() { } +CompositionText::CompositionText(const CompositionText& other) = default; + CompositionText::~CompositionText() { } diff --git a/chromium/ui/base/ime/composition_text.h b/chromium/ui/base/ime/composition_text.h index 9b729b502ba..ba56c627106 100644 --- a/chromium/ui/base/ime/composition_text.h +++ b/chromium/ui/base/ime/composition_text.h @@ -17,6 +17,7 @@ namespace ui { // A struct represents the status of an ongoing composition text. struct UI_BASE_IME_EXPORT CompositionText { CompositionText(); + CompositionText(const CompositionText& other); ~CompositionText(); bool operator==(const CompositionText& rhs) const { diff --git a/chromium/ui/base/ime/dummy_text_input_client.cc b/chromium/ui/base/ime/dummy_text_input_client.cc index 478b489b4c8..ac9b97343ab 100644 --- a/chromium/ui/base/ime/dummy_text_input_client.cc +++ b/chromium/ui/base/ime/dummy_text_input_client.cc @@ -3,15 +3,16 @@ // found in the LICENSE file. #include "ui/base/ime/dummy_text_input_client.h" +#include "ui/events/event.h" #include "ui/gfx/geometry/rect.h" namespace ui { DummyTextInputClient::DummyTextInputClient() - : text_input_type_(TEXT_INPUT_TYPE_NONE) {} + : text_input_type_(TEXT_INPUT_TYPE_NONE), insert_char_count_(0) {} DummyTextInputClient::DummyTextInputClient(TextInputType text_input_type) - : text_input_type_(text_input_type) {} + : text_input_type_(text_input_type), insert_char_count_(0) {} DummyTextInputClient::~DummyTextInputClient() { } @@ -29,7 +30,10 @@ void DummyTextInputClient::ClearCompositionText() { void DummyTextInputClient::InsertText(const base::string16& text) { } -void DummyTextInputClient::InsertChar(const KeyEvent& event) {} +void DummyTextInputClient::InsertChar(const KeyEvent& event) { + ++insert_char_count_; + last_insert_char_ = event.GetCharacter(); +} TextInputType DummyTextInputClient::GetTextInputType() const { return text_input_type_; diff --git a/chromium/ui/base/ime/dummy_text_input_client.h b/chromium/ui/base/ime/dummy_text_input_client.h index 3c5178215db..a1779939e44 100644 --- a/chromium/ui/base/ime/dummy_text_input_client.h +++ b/chromium/ui/base/ime/dummy_text_input_client.h @@ -49,9 +49,16 @@ class DummyTextInputClient : public TextInputClient { bool IsEditCommandEnabled(int command_id) override; void SetEditCommandForNextKeyEvent(int command_id) override; + int insert_char_count() const { return insert_char_count_; } + base::char16 last_insert_char() const { return last_insert_char_; } + TextInputType text_input_type_; DISALLOW_COPY_AND_ASSIGN(DummyTextInputClient); + + private: + int insert_char_count_; + base::char16 last_insert_char_; }; } // namespace ui diff --git a/chromium/ui/base/ime/ime_engine_handler_interface.cc b/chromium/ui/base/ime/ime_engine_handler_interface.cc deleted file mode 100644 index 695a547d5fc..00000000000 --- a/chromium/ui/base/ime/ime_engine_handler_interface.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "build/build_config.h" -#include "ui/base/ime/ime_engine_handler_interface.h" - -namespace ui { - -IMEEngineHandlerInterface::KeyboardEvent::KeyboardEvent() - : alt_key(false), ctrl_key(false), shift_key(false), caps_lock(false) {} - -IMEEngineHandlerInterface::KeyboardEvent::~KeyboardEvent() {} - -// ChromeOS only APIs. -#if defined(OS_CHROMEOS) - -IMEEngineHandlerInterface::MenuItem::MenuItem() {} - -IMEEngineHandlerInterface::MenuItem::~MenuItem() {} - -IMEEngineHandlerInterface::Candidate::Candidate() {} - -IMEEngineHandlerInterface::Candidate::~Candidate() {} - -namespace { -// The default entry number of a page in CandidateWindowProperty. -const int kDefaultPageSize = 9; -} // namespace - -// When the default values are changed, please modify -// CandidateWindow::CandidateWindowProperty defined in chromeos/ime/ too. -IMEEngineHandlerInterface::CandidateWindowProperty::CandidateWindowProperty() - : page_size(kDefaultPageSize), - is_cursor_visible(true), - is_vertical(false), - show_window_at_composition(false) {} - -IMEEngineHandlerInterface::CandidateWindowProperty::~CandidateWindowProperty() { -} - -#endif - -} // namespace ui diff --git a/chromium/ui/base/ime/ime_engine_handler_interface.h b/chromium/ui/base/ime/ime_engine_handler_interface.h index 6290841b3bd..3987085386a 100644 --- a/chromium/ui/base/ime/ime_engine_handler_interface.h +++ b/chromium/ui/base/ime/ime_engine_handler_interface.h @@ -54,33 +54,6 @@ class UI_BASE_IME_EXPORT IMEEngineHandlerInterface { int flags; }; - struct KeyboardEvent { - KeyboardEvent(); - virtual ~KeyboardEvent(); - - std::string type; - std::string key; - std::string code; - int key_code; // only used by on-screen keyboards. - std::string extension_id; - bool alt_key; - bool ctrl_key; - bool shift_key; - bool caps_lock; - }; - - enum SegmentStyle { - SEGMENT_STYLE_UNDERLINE, - SEGMENT_STYLE_DOUBLE_UNDERLINE, - SEGMENT_STYLE_NO_UNDERLINE, - }; - - struct SegmentInfo { - int start; - int end; - SegmentStyle style; - }; - virtual ~IMEEngineHandlerInterface() {} // Called when the Chrome input field get the focus. @@ -120,15 +93,6 @@ class UI_BASE_IME_EXPORT IMEEngineHandlerInterface { // If not, InputMethodChromeOS won't feed it with key events. virtual bool IsInterestedInKeyEvent() const = 0; - // Set the current composition and associated properties. - virtual bool SetComposition(int context_id, - const char* text, - int selection_start, - int selection_end, - int cursor, - const std::vector<SegmentInfo>& segments, - std::string* error) = 0; - // Clear the current composition. virtual bool ClearComposition(int context_id, std::string* error) = 0; @@ -138,10 +102,6 @@ class UI_BASE_IME_EXPORT IMEEngineHandlerInterface { const char* text, std::string* error) = 0; - // Send the sequence of key events. - virtual bool SendKeyEvents(int context_id, - const std::vector<KeyboardEvent>& events) = 0; - // Returns true if this IME is active, false if not. virtual bool IsActive() const = 0; @@ -159,77 +119,8 @@ class UI_BASE_IME_EXPORT IMEEngineHandlerInterface { int offset, size_t number_of_chars, std::string* error) = 0; - -// ChromeOS only APIs. #if defined(OS_CHROMEOS) - enum { - MENU_ITEM_MODIFIED_LABEL = 0x0001, - MENU_ITEM_MODIFIED_STYLE = 0x0002, - MENU_ITEM_MODIFIED_VISIBLE = 0x0004, - MENU_ITEM_MODIFIED_ENABLED = 0x0008, - MENU_ITEM_MODIFIED_CHECKED = 0x0010, - MENU_ITEM_MODIFIED_ICON = 0x0020, - }; - - enum MenuItemStyle { - MENU_ITEM_STYLE_NONE, - MENU_ITEM_STYLE_CHECK, - MENU_ITEM_STYLE_RADIO, - MENU_ITEM_STYLE_SEPARATOR, - }; - - enum CandidateWindowPosition { - WINDOW_POS_CURSOR, - WINDOW_POS_COMPOSITTION, - }; - - struct MenuItem { - MenuItem(); - virtual ~MenuItem(); - - std::string id; - std::string label; - MenuItemStyle style; - bool visible; - bool enabled; - bool checked; - - unsigned int modified; - std::vector<MenuItem> children; - }; - - struct UsageEntry { - std::string title; - std::string body; - }; - - struct Candidate { - Candidate(); - virtual ~Candidate(); - - std::string value; - int id; - std::string label; - std::string annotation; - UsageEntry usage; - std::vector<Candidate> candidates; - }; - - struct CandidateWindowProperty { - CandidateWindowProperty(); - virtual ~CandidateWindowProperty(); - int page_size; - bool is_cursor_visible; - bool is_vertical; - bool show_window_at_composition; - - // Auxiliary text is typically displayed in the footer of the candidate - // window. - std::string auxiliary_text; - bool is_auxiliary_text_visible; - }; - // Called when a property is activated or changed. virtual void PropertyActivate(const std::string& property_name) = 0; @@ -237,41 +128,21 @@ class UI_BASE_IME_EXPORT IMEEngineHandlerInterface { // based candidate index in lookup table. virtual void CandidateClicked(uint32_t index) = 0; - // This function returns the current property of the candidate window. - // The caller can use the returned value as the default property and - // modify some of specified items. - virtual const CandidateWindowProperty& GetCandidateWindowProperty() const = 0; - - // Change the property of the candidate window and repaint the candidate - // window widget. - virtual void SetCandidateWindowProperty( - const CandidateWindowProperty& property) = 0; - // Show or hide the candidate window. virtual bool SetCandidateWindowVisible(bool visible, std::string* error) = 0; - // Set the list of entries displayed in the candidate window. - virtual bool SetCandidates(int context_id, - const std::vector<Candidate>& candidates, - std::string* error) = 0; - // Set the position of the cursor in the candidate window. virtual bool SetCursorPosition(int context_id, int candidate_id, std::string* error) = 0; - - // Set the list of items that appears in the language menu when this IME is - // active. - virtual bool SetMenuItems(const std::vector<MenuItem>& items) = 0; - - // Update the state of the menu items. - virtual bool UpdateMenuItems(const std::vector<MenuItem>& items) = 0; - // Hides the input view window (from API call). virtual void HideInputView() = 0; -#endif // defined(OS_CHROMEOS) +#elif defined(OS_LINUX) || defined(OS_WIN) + // Get the id of the IME extension. + virtual std::string GetExtensionId() const = 0; +#endif // defined(OS_CHROMEOS) protected: IMEEngineHandlerInterface() {} }; diff --git a/chromium/ui/base/ime/ime_engine_observer.h b/chromium/ui/base/ime/ime_engine_observer.h deleted file mode 100644 index 7a2bf9d2682..00000000000 --- a/chromium/ui/base/ime/ime_engine_observer.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_IME_IME_ENGINE_OBSERVER_H_ -#define UI_BASE_IME_IME_ENGINE_OBSERVER_H_ - -#include "build/build_config.h" -#include "ui/base/ime/ime_engine_handler_interface.h" - -namespace ui { - -class IMEEngineObserver { - public: - virtual ~IMEEngineObserver() {} - - // Called when the IME becomes the active IME. - virtual void OnActivate(const std::string& engine_id) = 0; - - // Called when a text field gains focus, and will be sending key events. - virtual void OnFocus( - const IMEEngineHandlerInterface::InputContext& context) = 0; - - // Called when a text field loses focus, and will no longer generate events. - virtual void OnBlur(int context_id) = 0; - - // Called when the user pressed a key with a text field focused. - virtual void OnKeyEvent( - const std::string& engine_id, - const IMEEngineHandlerInterface::KeyboardEvent& event, - IMEEngineHandlerInterface::KeyEventDoneCallback& key_data) = 0; - - // Called when Chrome terminates on-going text input session. - virtual void OnReset(const std::string& engine_id) = 0; - - // Called when the IME is no longer active. - virtual void OnDeactivated(const std::string& engine_id) = 0; - - // Called when composition bounds are changed. - virtual void OnCompositionBoundsChanged( - const std::vector<gfx::Rect>& bounds) = 0; - - // Returns whether the observer is interested in key events. - virtual bool IsInterestedInKeyEvent() const = 0; - - // Called when a surrounding text is changed. - virtual void OnSurroundingTextChanged(const std::string& engine_id, - const std::string& text, - int cursor_pos, - int anchor_pos, - int offset_pos) = 0; - -// ChromeOS only APIs. -#if defined(OS_CHROMEOS) - - enum MouseButtonEvent { - MOUSE_BUTTON_LEFT, - MOUSE_BUTTON_RIGHT, - MOUSE_BUTTON_MIDDLE, - }; - - // Called when an InputContext's properties change while it is focused. - virtual void OnInputContextUpdate( - const IMEEngineHandlerInterface::InputContext& context) = 0; - - - - // Called when the user clicks on an item in the candidate list. - virtual void OnCandidateClicked(const std::string& engine_id, - int candidate_id, - MouseButtonEvent button) = 0; - - // Called when a menu item for this IME is interacted with. - virtual void OnMenuItemActivated(const std::string& engine_id, - const std::string& menu_id) = 0; -#endif -}; - -} // namespace ui - -#endif // UI_BASE_IME_IME_ENGINE_OBSERVER_H_ diff --git a/chromium/ui/base/ime/ime_input_context_handler_interface.h b/chromium/ui/base/ime/ime_input_context_handler_interface.h index 5a8f6aeae3a..b8fbb2b358b 100644 --- a/chromium/ui/base/ime/ime_input_context_handler_interface.h +++ b/chromium/ui/base/ime/ime_input_context_handler_interface.h @@ -10,6 +10,7 @@ #include <string> #include "ui/base/ime/composition_text.h" #include "ui/base/ime/ui_base_ime_export.h" +#include "ui/events/event.h" namespace ui { @@ -25,6 +26,9 @@ class UI_BASE_IME_EXPORT IMEInputContextHandlerInterface { // Called when the engine request deleting surrounding string. virtual void DeleteSurroundingText(int32_t offset, uint32_t length) = 0; + + // Called when the engine sends a key event. + virtual void SendKeyEvent(KeyEvent* event) = 0; }; } // namespace ui diff --git a/chromium/ui/base/ime/input_method_auralinux.cc b/chromium/ui/base/ime/input_method_auralinux.cc index 0b65ec31dc5..97ce8ea79b5 100644 --- a/chromium/ui/base/ime/input_method_auralinux.cc +++ b/chromium/ui/base/ime/input_method_auralinux.cc @@ -6,10 +6,22 @@ #include "base/auto_reset.h" #include "base/environment.h" +#include "ui/base/ime/ime_bridge.h" +#include "ui/base/ime/ime_engine_handler_interface.h" #include "ui/base/ime/linux/linux_input_method_context_factory.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/event.h" +namespace { + +ui::IMEEngineHandlerInterface* GetEngine() { + if (ui::IMEBridge::Get()) + return ui::IMEBridge::Get()->GetCurrentEngineHandler(); + return nullptr; +} + +} // namespace + namespace ui { InputMethodAuraLinux::InputMethodAuraLinux( @@ -17,7 +29,8 @@ InputMethodAuraLinux::InputMethodAuraLinux( : text_input_type_(TEXT_INPUT_TYPE_NONE), is_sync_mode_(false), composition_changed_(false), - suppress_next_result_(false) { + suppress_next_result_(false), + weak_ptr_factory_(this) { SetDelegate(delegate); context_ = LinuxInputMethodContextFactory::instance()->CreateInputMethodContext( @@ -52,6 +65,20 @@ void InputMethodAuraLinux::DispatchKeyEvent(ui::KeyEvent* event) { return; } + if (!event->HasNativeEvent() && sending_key_event_) { + // Faked key events that are sent from input.ime.sendKeyEvents. + ui::EventDispatchDetails details = DispatchKeyEventPostIME(event); + if (details.dispatcher_destroyed || details.target_destroyed || + event->stopped_propagation()) { + return; + } + if ((event->is_char() || event->GetDomKey().IsCharacter()) && + event->type() == ui::ET_KEY_PRESSED) { + GetTextInputClient()->InsertChar(*event); + } + return; + } + suppress_next_result_ = false; composition_changed_ = false; result_text_.clear(); @@ -67,6 +94,52 @@ void InputMethodAuraLinux::DispatchKeyEvent(ui::KeyEvent* event) { } } + // If there's an active IME extension is listening to the key event, and the + // current text input client is not password input client, the key event + // should be dispatched to the extension engine in the two conditions: + // 1) |filtered| == false: the ET_KEY_PRESSED event of non-character key, + // or the ET_KEY_RELEASED event of all key. + // 2) |filtered| == true && NeedInsertChar(): the ET_KEY_PRESSED event of + // character key. + if (text_input_type_ != TEXT_INPUT_TYPE_PASSWORD && + GetEngine() && GetEngine()->IsInterestedInKeyEvent() && + (!filtered || NeedInsertChar())) { + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::Bind( + &InputMethodAuraLinux::ProcessKeyEventByEngineDone, + weak_ptr_factory_.GetWeakPtr(), base::Owned(new ui::KeyEvent(*event)), + filtered, composition_changed_, + base::Owned(new ui::CompositionText(composition_)), + base::Owned(new base::string16(result_text_))); + GetEngine()->ProcessKeyEvent(*event, callback); + } else { + ProcessKeyEventDone(event, filtered, false); + } +} + +void InputMethodAuraLinux::ProcessKeyEventByEngineDone( + ui::KeyEvent* event, + bool filtered, + bool composition_changed, + ui::CompositionText* composition, + base::string16* result_text, + bool is_handled) { + composition_changed_ = composition_changed; + composition_.CopyFrom(*composition); + result_text_ = *result_text; + ProcessKeyEventDone(event, filtered, is_handled); +} + +void InputMethodAuraLinux::ProcessKeyEventDone(ui::KeyEvent* event, + bool filtered, + bool is_handled) { + DCHECK(event); + if (is_handled) + return; + + // If the IME extension has not handled the key event, passes the keyevent + // back to the previous processing flow. Preconditions for this situation: + // 1) |filtered| == false + // 2) |filtered| == true && NeedInsertChar() ui::EventDispatchDetails details; if (event->type() == ui::ET_KEY_PRESSED && filtered) { if (NeedInsertChar()) @@ -177,6 +250,21 @@ void InputMethodAuraLinux::UpdateContextFocusState() { context_simple_->Focus(); else context_simple_->Blur(); + + if (!ui::IMEBridge::Get()) // IMEBridge could be null for tests. + return; + + ui::IMEEngineHandlerInterface::InputContext context( + GetTextInputType(), GetTextInputMode(), GetTextInputFlags()); + ui::IMEBridge::Get()->SetCurrentInputContext(context); + + ui::IMEEngineHandlerInterface* engine = GetEngine(); + if (engine) { + if (old_text_input_type != TEXT_INPUT_TYPE_NONE) + engine->FocusOut(); + if (text_input_type_ != TEXT_INPUT_TYPE_NONE) + engine->FocusIn(context); + } } void InputMethodAuraLinux::OnTextInputTypeChanged( @@ -191,11 +279,19 @@ void InputMethodAuraLinux::OnCaretBoundsChanged(const TextInputClient* client) { return; NotifyTextInputCaretBoundsChanged(client); context_->SetCursorLocation(GetTextInputClient()->GetCaretBounds()); + + if (!IsTextInputTypeNone() && text_input_type_ != TEXT_INPUT_TYPE_PASSWORD && + GetEngine()) + GetEngine()->SetCompositionBounds(GetCompositionBounds(client)); } void InputMethodAuraLinux::CancelComposition(const TextInputClient* client) { if (!IsTextInputClientFocused(client)) return; + + if (GetEngine()) + GetEngine()->Reset(); + ResetContext(); } @@ -305,17 +401,6 @@ void InputMethodAuraLinux::OnPreeditEnd() { // Overridden from InputMethodBase. -void InputMethodAuraLinux::OnFocus() { - InputMethodBase::OnFocus(); - UpdateContextFocusState(); -} - -void InputMethodAuraLinux::OnBlur() { - ConfirmCompositionText(); - InputMethodBase::OnBlur(); - UpdateContextFocusState(); -} - void InputMethodAuraLinux::OnWillChangeFocusedClient( TextInputClient* focused_before, TextInputClient* focused) { @@ -358,9 +443,13 @@ ui::EventDispatchDetails InputMethodAuraLinux::SendFakeProcessKeyEvent( void InputMethodAuraLinux::ConfirmCompositionText() { TextInputClient* client = GetTextInputClient(); - if (client && client->HasCompositionText()) + if (client && client->HasCompositionText()) { client->ConfirmCompositionText(); + if (GetEngine()) + GetEngine()->Reset(); + } + ResetContext(); } diff --git a/chromium/ui/base/ime/input_method_auralinux.h b/chromium/ui/base/ime/input_method_auralinux.h index 29b76fdb44f..732d4340189 100644 --- a/chromium/ui/base/ime/input_method_auralinux.h +++ b/chromium/ui/base/ime/input_method_auralinux.h @@ -35,8 +35,6 @@ class UI_BASE_IME_EXPORT InputMethodAuraLinux void OnInputLocaleChanged() override; std::string GetInputLocale() override; bool IsCandidatePopupOpen() const override; - void OnFocus() override; - void OnBlur() override; // Overriden from ui::LinuxInputMethodContextDelegate void OnCommit(const base::string16& text) override; @@ -59,6 +57,22 @@ class UI_BASE_IME_EXPORT InputMethodAuraLinux void UpdateContextFocusState(); void ResetContext(); + // Processes the key event after the event is processed by the system IME or + // the extension. + void ProcessKeyEventDone(ui::KeyEvent* event, bool filtered, bool is_handled); + + // Callback function for IMEEngineHandlerInterface::ProcessKeyEvent(). + // It recovers the context when the event is being passed to the extension and + // call ProcessKeyEventDone() for the following processing. This is necessary + // as this method is async. The environment may be changed by other generated + // key events by the time the callback is run. + void ProcessKeyEventByEngineDone(ui::KeyEvent* event, + bool filtered, + bool composition_changed, + ui::CompositionText* composition, + base::string16* result_text, + bool is_handled); + scoped_ptr<LinuxInputMethodContext> context_; scoped_ptr<LinuxInputMethodContext> context_simple_; @@ -81,6 +95,9 @@ class UI_BASE_IME_EXPORT InputMethodAuraLinux // event will be discarded. bool suppress_next_result_; + // Used for making callbacks. + base::WeakPtrFactory<InputMethodAuraLinux> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(InputMethodAuraLinux); }; diff --git a/chromium/ui/base/ime/input_method_base.cc b/chromium/ui/base/ime/input_method_base.cc index df0cb118e53..282b6c4f9ad 100644 --- a/chromium/ui/base/ime/input_method_base.cc +++ b/chromium/ui/base/ime/input_method_base.cc @@ -7,6 +7,8 @@ #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/ime/ime_bridge.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/input_method_observer.h" #include "ui/base/ime/text_input_client.h" @@ -15,23 +17,33 @@ namespace ui { InputMethodBase::InputMethodBase() - : delegate_(NULL), - text_input_client_(NULL) { -} + : sending_key_event_(false), + delegate_(nullptr), + text_input_client_(nullptr) {} InputMethodBase::~InputMethodBase() { FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, OnInputMethodDestroyed(this)); + if (ui::IMEBridge::Get() && + ui::IMEBridge::Get()->GetInputContextHandler() == this) + ui::IMEBridge::Get()->SetInputContextHandler(nullptr); } void InputMethodBase::SetDelegate(internal::InputMethodDelegate* delegate) { delegate_ = delegate; } -void InputMethodBase::OnFocus() {} +void InputMethodBase::OnFocus() { + if (ui::IMEBridge::Get()) + ui::IMEBridge::Get()->SetInputContextHandler(this); +} -void InputMethodBase::OnBlur() {} +void InputMethodBase::OnBlur() { + if (ui::IMEBridge::Get() && + ui::IMEBridge::Get()->GetInputContextHandler() == this) + ui::IMEBridge::Get()->SetInputContextHandler(nullptr); +} void InputMethodBase::SetFocusedTextInputClient(TextInputClient* client) { SetFocusedTextInputClientInternal(client); @@ -40,7 +52,7 @@ void InputMethodBase::SetFocusedTextInputClient(TextInputClient* client) { void InputMethodBase::DetachTextInputClient(TextInputClient* client) { if (text_input_client_ != client) return; - SetFocusedTextInputClientInternal(NULL); + SetFocusedTextInputClientInternal(nullptr); } TextInputClient* InputMethodBase::GetTextInputClient() const { @@ -126,9 +138,69 @@ void InputMethodBase::SetFocusedTextInputClientInternal( if (old == client) return; OnWillChangeFocusedClient(old, client); - text_input_client_ = client; // NULL allowed. + text_input_client_ = client; // nullptr allowed. OnDidChangeFocusedClient(old, client); NotifyTextInputStateChanged(text_input_client_); } +std::vector<gfx::Rect> InputMethodBase::GetCompositionBounds( + const TextInputClient* client) { + std::vector<gfx::Rect> bounds; + if (client->HasCompositionText()) { + uint32_t i = 0; + gfx::Rect rect; + while (client->GetCompositionCharacterBounds(i++, &rect)) + bounds.push_back(rect); + } else { + // For case of no composition at present, use caret bounds which is required + // by the IME extension for certain features (e.g. physical keyboard + // auto-correct). + bounds.push_back(client->GetCaretBounds()); + } + return bounds; +} + +bool InputMethodBase::SendFakeProcessKeyEvent(bool pressed) const { + KeyEvent evt(pressed ? ET_KEY_PRESSED : ET_KEY_RELEASED, + pressed ? VKEY_PROCESSKEY : VKEY_UNKNOWN, EF_IME_FABRICATED_KEY); + ignore_result(DispatchKeyEventPostIME(&evt)); + return evt.stopped_propagation(); +} + +void InputMethodBase::CommitText(const std::string& text) { + if (text.empty() || !GetTextInputClient() || IsTextInputTypeNone()) + return; + + const base::string16 utf16_text = base::UTF8ToUTF16(text); + if (utf16_text.empty()) + return; + + if (!SendFakeProcessKeyEvent(true)) + GetTextInputClient()->InsertText(utf16_text); + SendFakeProcessKeyEvent(false); +} + +void InputMethodBase::UpdateCompositionText(const CompositionText& composition_, + uint32_t cursor_pos, + bool visible) { + if (IsTextInputTypeNone()) + return; + + if (!SendFakeProcessKeyEvent(true)) { + if (visible && !composition_.text.empty()) + GetTextInputClient()->SetCompositionText(composition_); + else + GetTextInputClient()->ClearCompositionText(); + } + SendFakeProcessKeyEvent(false); +} + +void InputMethodBase::DeleteSurroundingText(int32_t offset, uint32_t length) {} + +void InputMethodBase::SendKeyEvent(KeyEvent* event) { + sending_key_event_ = true; + DispatchKeyEvent(event); + sending_key_event_ = false; +} + } // namespace ui diff --git a/chromium/ui/base/ime/input_method_base.h b/chromium/ui/base/ime/input_method_base.h index e33461138f7..3a44e37f0ec 100644 --- a/chromium/ui/base/ime/input_method_base.h +++ b/chromium/ui/base/ime/input_method_base.h @@ -5,10 +5,13 @@ #ifndef UI_BASE_IME_INPUT_METHOD_BASE_H_ #define UI_BASE_IME_INPUT_METHOD_BASE_H_ +#include <vector> + #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "ui/base/ime/ime_input_context_handler_interface.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/ui_base_ime_export.h" #include "ui/events/event_dispatcher.h" @@ -27,7 +30,8 @@ class TextInputClient; // implementations. class UI_BASE_IME_EXPORT InputMethodBase : NON_EXPORTED_BASE(public InputMethod), - public base::SupportsWeakPtr<InputMethodBase> { + public base::SupportsWeakPtr<InputMethodBase>, + public IMEInputContextHandlerInterface { public: InputMethodBase(); ~InputMethodBase() override; @@ -59,6 +63,18 @@ class UI_BASE_IME_EXPORT InputMethodBase virtual void OnDidChangeFocusedClient(TextInputClient* focused_before, TextInputClient* focused) {} + // IMEInputContextHandlerInterface: + void CommitText(const std::string& text) override; + void UpdateCompositionText(const CompositionText& text, + uint32_t cursor_pos, + bool visible) override; + void DeleteSurroundingText(int32_t offset, uint32_t length) override; + void SendKeyEvent(KeyEvent* event) override; + + // Sends a fake key event for IME composing without physical key events. + // Returns true if the faked key event is stopped propagation. + bool SendFakeProcessKeyEvent(bool pressed) const; + // Returns true if |client| is currently focused. bool IsTextInputClientFocused(const TextInputClient* client); @@ -84,6 +100,13 @@ class UI_BASE_IME_EXPORT InputMethodBase // |client| which is the text input client with focus. void NotifyTextInputCaretBoundsChanged(const TextInputClient* client); + // Gets the bounds of the composition text or cursor in |client|. + std::vector<gfx::Rect> GetCompositionBounds(const TextInputClient* client); + + // Indicates whether the IME extension is currently sending a fake key event. + // This is used in SendKeyEvent. + bool sending_key_event_; + private: void SetFocusedTextInputClientInternal(TextInputClient* client); diff --git a/chromium/ui/base/ime/input_method_chromeos.cc b/chromium/ui/base/ime/input_method_chromeos.cc index 59077b451f0..924c9cf9683 100644 --- a/chromium/ui/base/ime/input_method_chromeos.cc +++ b/chromium/ui/base/ime/input_method_chromeos.cc @@ -57,17 +57,6 @@ InputMethodChromeOS::~InputMethodChromeOS() { ui::IMEBridge::Get()->SetInputContextHandler(NULL); } -void InputMethodChromeOS::OnFocus() { - InputMethodBase::OnFocus(); - OnTextInputTypeChanged(GetTextInputClient()); -} - -void InputMethodChromeOS::OnBlur() { - ConfirmCompositionText(); - InputMethodBase::OnBlur(); - OnTextInputTypeChanged(GetTextInputClient()); -} - bool InputMethodChromeOS::OnUntranslatedIMEMessage( const base::NativeEvent& event, NativeEventResult* result) { @@ -180,33 +169,24 @@ void InputMethodChromeOS::OnCaretBoundsChanged(const TextInputClient* client) { // The current text input type should not be NONE if |context_| is focused. DCHECK(client == GetTextInputClient()); DCHECK(!IsTextInputTypeNone()); - const gfx::Rect caret_rect = client->GetCaretBounds(); - - gfx::Rect composition_head; - std::vector<gfx::Rect> rects; - if (client->HasCompositionText()) { - uint32_t i = 0; - gfx::Rect rect; - while (client->GetCompositionCharacterBounds(i++, &rect)) - rects.push_back(rect); - } - - // Pepper don't support composition bounds, so fallback to caret bounds to - // avoid bad user experience (the IME window moved to upper left corner). - // For case of no composition at present, also use caret bounds which is - // required by the IME extension for certain features (e.g. physical keyboard - // autocorrect). - if (rects.empty()) - rects.push_back(caret_rect); - composition_head = rects[0]; if (GetEngine()) - GetEngine()->SetCompositionBounds(rects); + GetEngine()->SetCompositionBounds(GetCompositionBounds(client)); chromeos::IMECandidateWindowHandlerInterface* candidate_window = ui::IMEBridge::Get()->GetCandidateWindowHandler(); if (!candidate_window) return; + + const gfx::Rect caret_rect = client->GetCaretBounds(); + + // Pepper doesn't support composition bounds, so fall back to caret bounds to + // avoid a bad user experience (the IME window moved to upper left corner). + gfx::Rect composition_head; + if (client->HasCompositionText()) + client->GetCompositionCharacterBounds(0, &composition_head); + else + composition_head = caret_rect; candidate_window->SetCursorBounds(caret_rect, composition_head); gfx::Range text_range; @@ -381,7 +361,10 @@ void InputMethodChromeOS::ProcessFilteredKeyPressEvent(ui::KeyEvent* event) { } ui::KeyEvent fabricated_event(ET_KEY_PRESSED, VKEY_PROCESSKEY, - event->flags()); + event->code(), + event->flags(), + event->GetDomKey(), + event->time_stamp()); ignore_result(DispatchKeyEventPostIME(&fabricated_event)); if (fabricated_event.stopped_propagation()) event->StopPropagation(); @@ -459,14 +442,6 @@ bool InputMethodChromeOS::HasInputMethodResult() const { return result_text_.length() || composition_changed_; } -bool InputMethodChromeOS::SendFakeProcessKeyEvent(bool pressed) const { - KeyEvent evt(pressed ? ET_KEY_PRESSED : ET_KEY_RELEASED, - pressed ? VKEY_PROCESSKEY : VKEY_UNKNOWN, - EF_IME_FABRICATED_KEY); - ignore_result(DispatchKeyEventPostIME(&evt)); - return evt.stopped_propagation(); -} - void InputMethodChromeOS::CommitText(const std::string& text) { if (text.empty()) return; diff --git a/chromium/ui/base/ime/input_method_chromeos.h b/chromium/ui/base/ime/input_method_chromeos.h index 28da9b7c9bb..343e85dec84 100644 --- a/chromium/ui/base/ime/input_method_chromeos.h +++ b/chromium/ui/base/ime/input_method_chromeos.h @@ -22,16 +22,12 @@ namespace ui { // A ui::InputMethod implementation based on IBus. -class UI_BASE_IME_EXPORT InputMethodChromeOS - : public InputMethodBase, - public ui::IMEInputContextHandlerInterface { +class UI_BASE_IME_EXPORT InputMethodChromeOS : public InputMethodBase { public: explicit InputMethodChromeOS(internal::InputMethodDelegate* delegate); ~InputMethodChromeOS() override; // Overridden from InputMethod: - void OnFocus() override; - void OnBlur() override; bool OnUntranslatedIMEMessage(const base::NativeEvent& event, NativeEventResult* result) override; void DispatchKeyEvent(ui::KeyEvent* event) override; @@ -91,10 +87,6 @@ class UI_BASE_IME_EXPORT InputMethodChromeOS // Checks if there is pending input method result. bool HasInputMethodResult() const; - // Sends a fake key event for IME composing without physical key events. - // Returns true if the faked key event is stopped propagation. - bool SendFakeProcessKeyEvent(bool pressed) const; - // Passes keyevent and executes character composition if necessary. Returns // true if character composer comsumes key event. bool ExecuteCharacterComposer(const ui::KeyEvent& event); diff --git a/chromium/ui/base/ime/input_method_chromeos_unittest.cc b/chromium/ui/base/ime/input_method_chromeos_unittest.cc index d60d99ec038..8ee42945df7 100644 --- a/chromium/ui/base/ime/input_method_chromeos_unittest.cc +++ b/chromium/ui/base/ime/input_method_chromeos_unittest.cc @@ -345,12 +345,10 @@ class InputMethodChromeOSTest : public internal::InputMethodDelegate, TEST_F(InputMethodChromeOSTest, GetInputLocale) { // ui::InputMethodChromeOS does not support the API. - ime_->OnFocus(); EXPECT_EQ("", ime_->GetInputLocale()); } TEST_F(InputMethodChromeOSTest, GetInputTextType) { - ime_->OnFocus(); EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType()); input_type_ = TEXT_INPUT_TYPE_PASSWORD; ime_->OnTextInputTypeChanged(this); @@ -361,7 +359,6 @@ TEST_F(InputMethodChromeOSTest, GetInputTextType) { } TEST_F(InputMethodChromeOSTest, CanComposeInline) { - ime_->OnFocus(); EXPECT_TRUE(ime_->CanComposeInline()); can_compose_inline_ = false; ime_->OnTextInputTypeChanged(this); @@ -369,14 +366,12 @@ TEST_F(InputMethodChromeOSTest, CanComposeInline) { } TEST_F(InputMethodChromeOSTest, GetTextInputClient) { - ime_->OnFocus(); EXPECT_EQ(this, ime_->GetTextInputClient()); ime_->SetFocusedTextInputClient(NULL); EXPECT_EQ(NULL, ime_->GetTextInputClient()); } TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) { - ime_->OnFocus(); EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType()); ime_->SetFocusedTextInputClient(NULL); input_type_ = TEXT_INPUT_TYPE_PASSWORD; @@ -393,7 +388,6 @@ TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) { // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is // TEXT. TEST_F(InputMethodChromeOSTest, FocusIn_Text) { - ime_->OnFocus(); // A context shouldn't be created since the daemon is not running. EXPECT_EQ(0U, on_input_method_changed_call_count_); // Click a text input form. @@ -413,7 +407,6 @@ TEST_F(InputMethodChromeOSTest, FocusIn_Text) { // Confirm that InputMethodEngine::FocusIn is called on "connected" even if // input_type_ is PASSWORD. TEST_F(InputMethodChromeOSTest, FocusIn_Password) { - ime_->OnFocus(); EXPECT_EQ(0U, on_input_method_changed_call_count_); input_type_ = TEXT_INPUT_TYPE_PASSWORD; ime_->OnTextInputTypeChanged(this); @@ -425,7 +418,7 @@ TEST_F(InputMethodChromeOSTest, FocusIn_Password) { // Confirm that IBusClient::FocusOut is called as expected. TEST_F(InputMethodChromeOSTest, FocusOut_None) { input_type_ = TEXT_INPUT_TYPE_TEXT; - ime_->OnFocus(); + ime_->OnTextInputTypeChanged(this); EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count()); EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count()); input_type_ = TEXT_INPUT_TYPE_NONE; @@ -437,7 +430,7 @@ TEST_F(InputMethodChromeOSTest, FocusOut_None) { // Confirm that IBusClient::FocusOut is called as expected. TEST_F(InputMethodChromeOSTest, FocusOut_Password) { input_type_ = TEXT_INPUT_TYPE_TEXT; - ime_->OnFocus(); + ime_->OnTextInputTypeChanged(this); EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count()); EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count()); input_type_ = TEXT_INPUT_TYPE_PASSWORD; @@ -448,7 +441,6 @@ TEST_F(InputMethodChromeOSTest, FocusOut_Password) { // FocusIn/FocusOut scenario test TEST_F(InputMethodChromeOSTest, Focus_Scenario) { - ime_->OnFocus(); // Confirm that both FocusIn and FocusOut are NOT called. EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count()); EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count()); @@ -503,7 +495,7 @@ TEST_F(InputMethodChromeOSTest, Focus_Scenario) { // Test if the new |caret_bounds_| is correctly sent to ibus-daemon. TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) { input_type_ = TEXT_INPUT_TYPE_TEXT; - ime_->OnFocus(); + ime_->OnTextInputTypeChanged(this); EXPECT_EQ( 1, mock_ime_candidate_window_handler_->set_cursor_bounds_call_count()); @@ -721,7 +713,6 @@ TEST_F(InputMethodChromeOSTest, } TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) { - ime_->OnFocus(); // Click a text input form. input_type_ = TEXT_INPUT_TYPE_TEXT; ime_->OnTextInputTypeChanged(this); @@ -749,7 +740,6 @@ TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) { } TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) { - ime_->OnFocus(); // Click a text input form. input_type_ = TEXT_INPUT_TYPE_TEXT; ime_->OnTextInputTypeChanged(this); @@ -776,7 +766,6 @@ TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) { } TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) { - ime_->OnFocus(); // Click a text input form. input_type_ = TEXT_INPUT_TYPE_TEXT; ime_->OnTextInputTypeChanged(this); @@ -802,7 +791,6 @@ TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) { } TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) { - ime_->OnFocus(); // Click a text input form. input_type_ = TEXT_INPUT_TYPE_TEXT; ime_->OnTextInputTypeChanged(this); @@ -831,11 +819,6 @@ class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest { InputMethodChromeOSKeyEventTest() {} ~InputMethodChromeOSKeyEventTest() override {} - void SetUp() override { - InputMethodChromeOSTest::SetUp(); - ime_->OnFocus(); - } - DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest); }; @@ -968,4 +951,27 @@ TEST_F(InputMethodChromeOSKeyEventTest, StopPropagationTest) { EXPECT_EQ(L'A', inserted_char_); } +TEST_F(InputMethodChromeOSKeyEventTest, DeadKeyPressTest) { + // Preparation. + input_type_ = TEXT_INPUT_TYPE_TEXT; + ime_->OnTextInputTypeChanged(this); + + ui::KeyEvent eventA(ET_KEY_PRESSED, + VKEY_OEM_4, // '[' + DomCode::BRACKET_LEFT, + 0, + DomKey::DeadKeyFromCombiningCharacter('^'), + EventTimeForNow()); + ime_->ProcessKeyEventPostIME(&eventA, true); + + const ui::KeyEvent& key_event = dispatched_key_event_; + + EXPECT_EQ(ET_KEY_PRESSED, key_event.type()); + EXPECT_EQ(VKEY_PROCESSKEY, key_event.key_code()); + EXPECT_EQ(eventA.code(), key_event.code()); + EXPECT_EQ(eventA.flags(), key_event.flags()); + EXPECT_EQ(eventA.GetDomKey(), key_event.GetDomKey()); + EXPECT_EQ(eventA.time_stamp(), key_event.time_stamp()); +} + } // namespace ui diff --git a/chromium/ui/base/ime/input_method_factory.cc b/chromium/ui/base/ime/input_method_factory.cc index dd9d31a6665..1fda6daaad7 100644 --- a/chromium/ui/base/ime/input_method_factory.cc +++ b/chromium/ui/base/ime/input_method_factory.cc @@ -11,7 +11,6 @@ #include "ui/base/ime/input_method_chromeos.h" #elif defined(OS_WIN) #include "ui/base/ime/input_method_win.h" -#include "ui/base/ime/remote_input_method_win.h" #elif defined(OS_MACOSX) #include "ui/base/ime/input_method_mac.h" #elif defined(USE_AURA) && defined(OS_LINUX) && defined(USE_X11) && \ @@ -53,8 +52,6 @@ scoped_ptr<InputMethod> CreateInputMethod( #if defined(OS_CHROMEOS) return make_scoped_ptr(new InputMethodChromeOS(delegate)); #elif defined(OS_WIN) - if (IsRemoteInputMethodWinRequired(widget)) - return CreateRemoteInputMethodWin(delegate); return make_scoped_ptr(new InputMethodWin(delegate, widget)); #elif defined(OS_MACOSX) return make_scoped_ptr(new InputMethodMac(delegate)); diff --git a/chromium/ui/base/ime/input_method_win.cc b/chromium/ui/base/ime/input_method_win.cc index 6604c6d31bb..df9ac66f233 100644 --- a/chromium/ui/base/ime/input_method_win.cc +++ b/chromium/ui/base/ime/input_method_win.cc @@ -6,9 +6,12 @@ #include <stddef.h> #include <stdint.h> +#include <cwctype> #include "base/auto_reset.h" #include "base/command_line.h" +#include "ui/base/ime/ime_bridge.h" +#include "ui/base/ime/ime_engine_handler_interface.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/win/tsf_input_scope.h" #include "ui/base/ui_base_switches.h" @@ -19,6 +22,16 @@ #include "ui/gfx/win/dpi.h" #include "ui/gfx/win/hwnd_util.h" +namespace { + +ui::IMEEngineHandlerInterface* GetEngine() { + if (ui::IMEBridge::Get()) + return ui::IMEBridge::Get()->GetCurrentEngineHandler(); + return nullptr; +} + +} // namespace + namespace ui { namespace { @@ -35,25 +48,12 @@ InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate, accept_carriage_return_(false), enabled_(false), is_candidate_popup_open_(false), - composing_window_handle_(NULL) { + composing_window_handle_(NULL), + weak_ptr_factory_(this) { SetDelegate(delegate); } -void InputMethodWin::OnFocus() { - InputMethodBase::OnFocus(); - if (GetTextInputClient()) - UpdateIMEState(); -} - -void InputMethodWin::OnBlur() { - ConfirmCompositionText(); - // Gets the focused text input client before calling parent's OnBlur() because - // it will cause GetTextInputClient() returns NULL. - ui::TextInputClient* client = GetTextInputClient(); - InputMethodBase::OnBlur(); - if (client) - UpdateIMEState(); -} +InputMethodWin::~InputMethodWin() {} bool InputMethodWin::OnUntranslatedIMEMessage( const base::NativeEvent& event, @@ -167,19 +167,45 @@ void InputMethodWin::DispatchKeyEvent(ui::KeyEvent* event) { } // If only 1 WM_CHAR per the key event, set it as the character of it. - if (char_msgs.size() == 1) + if (char_msgs.size() == 1 && + !std::iswcntrl(static_cast<wint_t>(char_msgs[0].wParam))) event->set_character(static_cast<base::char16>(char_msgs[0].wParam)); + // Dispatches the key events to the Chrome IME extension which is listening to + // key events on the following two situations: + // 1) |char_msgs| is empty when the event is non-character key. + // 2) |char_msgs|.size() == 1 when the event is character key and the WM_CHAR + // messages have been combined in the event processing flow. + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableMergeKeyCharEvents) && + char_msgs.size() <= 1 && GetEngine() && + GetEngine()->IsInterestedInKeyEvent()) { + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::Bind( + &InputMethodWin::ProcessKeyEventDone, weak_ptr_factory_.GetWeakPtr(), + base::Owned(new ui::KeyEvent(*event)), + base::Owned(new std::vector<MSG>(char_msgs))); + GetEngine()->ProcessKeyEvent(*event, callback); + } else { + ProcessKeyEventDone(event, &char_msgs, false); + } +} + +void InputMethodWin::ProcessKeyEventDone(ui::KeyEvent* event, + const std::vector<MSG>* char_msgs, + bool is_handled) { + DCHECK(event); + if (is_handled) + return; + ui::EventDispatchDetails details = DispatchKeyEventPostIME(event); if (details.dispatcher_destroyed || details.target_destroyed || event->stopped_propagation()) { return; } - for (size_t i = 0; i < char_msgs.size(); ++i) { - MSG msg = char_msgs[i]; + BOOL handled; + for (const auto& msg : (*char_msgs)) OnChar(msg.hwnd, msg.message, msg.wParam, msg.lParam, msg, &handled); - } } void InputMethodWin::OnTextInputTypeChanged(const TextInputClient* client) { @@ -190,10 +216,19 @@ void InputMethodWin::OnTextInputTypeChanged(const TextInputClient* client) { } void InputMethodWin::OnCaretBoundsChanged(const TextInputClient* client) { - if (!enabled_ || !IsTextInputClientFocused(client) || - !IsWindowFocused(client)) { + if (!IsTextInputClientFocused(client) || !IsWindowFocused(client)) return; + TextInputType text_input_type = GetTextInputType(); + if (client == GetTextInputClient() && + text_input_type != TEXT_INPUT_TYPE_NONE && + text_input_type != TEXT_INPUT_TYPE_PASSWORD && GetEngine()) { + // |enabled_| == false could be faked, and the engine should rely on the + // real type from GetTextInputType(). + GetEngine()->SetCompositionBounds(GetCompositionBounds(client)); } + if (!enabled_) + return; + // The current text input type should not be NONE if |client| is focused. DCHECK(!IsTextInputTypeNone()); // Tentatively assume that the returned value is DIP (Density Independent @@ -214,8 +249,18 @@ void InputMethodWin::OnCaretBoundsChanged(const TextInputClient* client) { } void InputMethodWin::CancelComposition(const TextInputClient* client) { - if (enabled_ && IsTextInputClientFocused(client)) - imm32_manager_.CancelIME(toplevel_window_handle_); + if (IsTextInputClientFocused(client)) { + // |enabled_| == false could be faked, and the engine should rely on the + // real type get from GetTextInputType(). + TextInputType text_input_type = GetTextInputType(); + if (text_input_type != TEXT_INPUT_TYPE_NONE && + text_input_type != TEXT_INPUT_TYPE_PASSWORD && GetEngine()) { + GetEngine()->Reset(); + } + + if (enabled_) + imm32_manager_.CancelIME(toplevel_window_handle_); + } } void InputMethodWin::OnInputLocaleChanged() { @@ -591,27 +636,30 @@ bool InputMethodWin::IsWindowFocused(const TextInputClient* client) const { } void InputMethodWin::DispatchFabricatedKeyEvent(ui::KeyEvent* event) { - if (event->is_char()) { - if (GetTextInputClient()) { - ui::KeyEvent ch_event(*event); - ch_event.set_character(static_cast<base::char16>(event->key_code())); - GetTextInputClient()->InsertChar(ch_event); - return; - } + // The key event if from calling input.ime.sendKeyEvent or test. + ui::EventDispatchDetails details = DispatchKeyEventPostIME(event); + if (details.dispatcher_destroyed || details.target_destroyed || + event->stopped_propagation()) { + return; } - ignore_result(DispatchKeyEventPostIME(event)); + + if ((event->is_char() || event->GetDomKey().IsCharacter()) && + event->type() == ui::ET_KEY_PRESSED && GetTextInputClient()) + GetTextInputClient()->InsertChar(*event); } void InputMethodWin::ConfirmCompositionText() { if (composing_window_handle_) imm32_manager_.CleanupComposition(composing_window_handle_); - if (!IsTextInputTypeNone()) { - // Though above line should confirm the client's composition text by sending - // a result text to us, in case the input method and the client are in - // inconsistent states, we check the client's composition state again. - if (GetTextInputClient()->HasCompositionText()) - GetTextInputClient()->ConfirmCompositionText(); + // Though above line should confirm the client's composition text by sending a + // result text to us, in case the input method and the client are in + // inconsistent states, we check the client's composition state again. + if (!IsTextInputTypeNone() && GetTextInputClient()->HasCompositionText()) { + GetTextInputClient()->ConfirmCompositionText(); + + if (GetEngine()) + GetEngine()->Reset(); } } @@ -619,7 +667,10 @@ void InputMethodWin::UpdateIMEState() { // Use switch here in case we are going to add more text input types. // We disable input method in password field. const HWND window_handle = toplevel_window_handle_; - const TextInputType text_input_type = GetTextInputType(); + const TextInputType text_input_type = + (GetEngine() && GetEngine()->IsInterestedInKeyEvent()) + ? TEXT_INPUT_TYPE_NONE + : GetTextInputType(); const TextInputMode text_input_mode = GetTextInputMode(); switch (text_input_type) { case ui::TEXT_INPUT_TYPE_NONE: @@ -636,6 +687,23 @@ void InputMethodWin::UpdateIMEState() { imm32_manager_.SetTextInputMode(window_handle, text_input_mode); tsf_inputscope::SetInputScopeForTsfUnawareWindow( window_handle, text_input_type, text_input_mode); + + if (!ui::IMEBridge::Get()) // IMEBridge could be null for tests. + return; + + const TextInputType old_text_input_type = + ui::IMEBridge::Get()->GetCurrentInputContext().type; + ui::IMEEngineHandlerInterface::InputContext context( + GetTextInputType(), GetTextInputMode(), GetTextInputFlags()); + ui::IMEBridge::Get()->SetCurrentInputContext(context); + + ui::IMEEngineHandlerInterface* engine = GetEngine(); + if (engine) { + if (old_text_input_type != ui::TEXT_INPUT_TYPE_NONE) + engine->FocusOut(); + if (GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) + engine->FocusIn(context); + } } } // namespace ui diff --git a/chromium/ui/base/ime/input_method_win.h b/chromium/ui/base/ime/input_method_win.h index ce411491e23..742085445e3 100644 --- a/chromium/ui/base/ime/input_method_win.h +++ b/chromium/ui/base/ime/input_method_win.h @@ -21,10 +21,9 @@ class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { public: InputMethodWin(internal::InputMethodDelegate* delegate, HWND toplevel_window_handle); + ~InputMethodWin() override; // Overridden from InputMethod: - void OnFocus() override; - void OnBlur() override; bool OnUntranslatedIMEMessage(const base::NativeEvent& event, NativeEventResult* result) override; void DispatchKeyEvent(ui::KeyEvent* event) override; @@ -101,6 +100,11 @@ class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { // Enables or disables the IME according to the current text input type. void UpdateIMEState(); + // Callback function for IMEEngineHandlerInterface::ProcessKeyEvent. + void ProcessKeyEventDone(ui::KeyEvent* event, + const std::vector<MSG>* char_msgs, + bool is_handled); + // Windows IMM32 wrapper. // (See "ui/base/ime/win/ime_input.h" for its details.) ui::IMM32Manager imm32_manager_; @@ -130,6 +134,9 @@ class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { // composition. HWND composing_window_handle_; + // Used for making callbacks. + base::WeakPtrFactory<InputMethodWin> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(InputMethodWin); }; diff --git a/chromium/ui/base/ime/remote_input_method_delegate_win.h b/chromium/ui/base/ime/remote_input_method_delegate_win.h deleted file mode 100644 index a172c7b5100..00000000000 --- a/chromium/ui/base/ime/remote_input_method_delegate_win.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_IME_REMOTE_INPUT_METHOD_DELEGATE_WIN_H_ -#define UI_BASE_IME_REMOTE_INPUT_METHOD_DELEGATE_WIN_H_ - -#include <stdint.h> - -#include <vector> - -#include "ui/base/ime/ui_base_ime_export.h" -#include "ui/gfx/geometry/rect.h" - -namespace ui { -namespace internal { - -// An interface implemented by the object to forward events that should be -// handled by the IME which is running in the remote metro_driver process. -class UI_BASE_IME_EXPORT RemoteInputMethodDelegateWin { - public: - virtual ~RemoteInputMethodDelegateWin() {} - - // Notifies that composition should be canceled (if any). - virtual void CancelComposition() = 0; - - // Notifies that properties of the focused TextInputClient is changed. - // Note that an empty |input_scopes| represents that TextInputType is - // TEXT_INPUT_TYPE_NONE. - // Caveats: |input_scopes| is defined as std::vector<int32_t> rather than - // std::vector<InputScope> because the wire format of IPC message - // MetroViewerHostMsg_ImeTextInputClientUpdated uses std::vector<int32_t> to - // avoid dependency on <InputScope.h> header. - virtual void OnTextInputClientUpdated( - const std::vector<int32_t>& input_scopes, - const std::vector<gfx::Rect>& composition_character_bounds) = 0; -}; - -} // namespace internal -} // namespace ui - -#endif // UI_BASE_IME_REMOTE_INPUT_METHOD_DELEGATE_WIN_H_ diff --git a/chromium/ui/base/ime/remote_input_method_win.cc b/chromium/ui/base/ime/remote_input_method_win.cc deleted file mode 100644 index 27d242a384b..00000000000 --- a/chromium/ui/base/ime/remote_input_method_win.cc +++ /dev/null @@ -1,385 +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 "ui/base/ime/remote_input_method_win.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/command_line.h" -#include "base/macros.h" -#include "base/observer_list.h" -#include "base/strings/utf_string_conversions.h" -#include "base/win/scoped_handle.h" -#include "ui/base/ime/input_method.h" -#include "ui/base/ime/input_method_delegate.h" -#include "ui/base/ime/input_method_observer.h" -#include "ui/base/ime/remote_input_method_delegate_win.h" -#include "ui/base/ime/text_input_client.h" -#include "ui/base/ime/win/tsf_input_scope.h" -#include "ui/base/ui_base_switches.h" -#include "ui/events/event.h" -#include "ui/events/event_utils.h" -#include "ui/gfx/geometry/rect.h" - -namespace ui { -namespace { - -const LANGID kFallbackLangID = - MAKELANGID(LANG_NEUTRAL, SUBLANG_UI_CUSTOM_DEFAULT); - -InputMethod* g_public_interface_ = NULL; -RemoteInputMethodPrivateWin* g_private_interface_ = NULL; - -void RegisterInstance(InputMethod* public_interface, - RemoteInputMethodPrivateWin* private_interface) { - CHECK(g_public_interface_ == NULL) - << "Only one instance is supported at the same time"; - CHECK(g_private_interface_ == NULL) - << "Only one instance is supported at the same time"; - g_public_interface_ = public_interface; - g_private_interface_ = private_interface; -} - -RemoteInputMethodPrivateWin* GetPrivate(InputMethod* public_interface) { - if (g_public_interface_ != public_interface) - return NULL; - return g_private_interface_; -} - -void UnregisterInstance(InputMethod* public_interface) { - RemoteInputMethodPrivateWin* private_interface = GetPrivate(public_interface); - if (g_public_interface_ == public_interface && - g_private_interface_ == private_interface) { - g_public_interface_ = NULL; - g_private_interface_ = NULL; - } -} - -std::string GetLocaleString(LCID Locale_id, LCTYPE locale_type) { - wchar_t buffer[16] = {}; - - //|chars_written| includes NUL terminator. - const int chars_written = - GetLocaleInfo(Locale_id, locale_type, buffer, arraysize(buffer)); - if (chars_written <= 1 || static_cast<int>(arraysize(buffer)) < chars_written) - return std::string(); - std::string result; - base::WideToUTF8(buffer, chars_written - 1, &result); - return result; -} - -std::vector<int32_t> GetInputScopesAsInt(TextInputType text_input_type, - TextInputMode text_input_mode) { - std::vector<int32_t> result; - // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE. - if (text_input_type == TEXT_INPUT_TYPE_NONE) - return result; - - const std::vector<InputScope>& input_scopes = - tsf_inputscope::GetInputScopes(text_input_type, text_input_mode); - result.reserve(input_scopes.size()); - for (size_t i = 0; i < input_scopes.size(); ++i) - result.push_back(static_cast<int32_t>(input_scopes[i])); - return result; -} - -std::vector<gfx::Rect> GetCompositionCharacterBounds( - const TextInputClient* client) { - if (!client) - return std::vector<gfx::Rect>(); - - std::vector<gfx::Rect> bounds; - if (client->HasCompositionText()) { - gfx::Range range; - if (client->GetCompositionTextRange(&range)) { - for (uint32_t i = 0; i < range.length(); ++i) { - gfx::Rect rect; - if (!client->GetCompositionCharacterBounds(i, &rect)) - break; - bounds.push_back(rect); - } - } - } - - // Use the caret bounds as a fallback if no composition character bounds is - // available. One typical use case is PPAPI Flash, which does not support - // GetCompositionCharacterBounds at all. crbug.com/133472 - if (bounds.empty()) - bounds.push_back(client->GetCaretBounds()); - return bounds; -} - -class RemoteInputMethodWin : public InputMethod, - public RemoteInputMethodPrivateWin { - public: - explicit RemoteInputMethodWin(internal::InputMethodDelegate* delegate) - : delegate_(delegate), - remote_delegate_(NULL), - text_input_client_(NULL), - is_candidate_popup_open_(false), - is_ime_(false), - langid_(kFallbackLangID) { - RegisterInstance(this, this); - } - - ~RemoteInputMethodWin() override { - FOR_EACH_OBSERVER(InputMethodObserver, - observer_list_, - OnInputMethodDestroyed(this)); - UnregisterInstance(this); - } - - private: - // Overridden from InputMethod: - void SetDelegate(internal::InputMethodDelegate* delegate) override { - delegate_ = delegate; - } - - void OnFocus() override {} - - void OnBlur() override {} - - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) override { - return false; - } - - void SetFocusedTextInputClient(TextInputClient* client) override { - std::vector<int32_t> prev_input_scopes; - std::swap(input_scopes_, prev_input_scopes); - std::vector<gfx::Rect> prev_bounds; - std::swap(composition_character_bounds_, prev_bounds); - if (client) { - input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), - client->GetTextInputMode()); - composition_character_bounds_ = GetCompositionCharacterBounds(client); - } - - const bool text_input_client_changed = text_input_client_ != client; - text_input_client_ = client; - if (text_input_client_changed) { - FOR_EACH_OBSERVER(InputMethodObserver, - observer_list_, - OnTextInputStateChanged(client)); - } - - if (!remote_delegate_ || (prev_input_scopes == input_scopes_ && - prev_bounds == composition_character_bounds_)) - return; - remote_delegate_->OnTextInputClientUpdated(input_scopes_, - composition_character_bounds_); - } - - void DetachTextInputClient(TextInputClient* client) override { - if (text_input_client_ != client) - return; - SetFocusedTextInputClient(NULL); - } - - TextInputClient* GetTextInputClient() const override { - return text_input_client_; - } - - void DispatchKeyEvent(ui::KeyEvent* event) override { - if (event->HasNativeEvent()) { - const base::NativeEvent& native_key_event = event->native_event(); - if (native_key_event.message == WM_CHAR && text_input_client_) { - text_input_client_->InsertChar(*event); - event->StopPropagation(); - } - return; - } - - if (event->is_char()) { - if (text_input_client_) { - text_input_client_->InsertChar(*event); - } - event->StopPropagation(); - return; - } - if (delegate_) - ignore_result(delegate_->DispatchKeyEventPostIME(event)); - } - - void OnTextInputTypeChanged(const TextInputClient* client) override { - if (!text_input_client_ || text_input_client_ != client) - return; - std::vector<int32_t> prev_input_scopes; - std::swap(input_scopes_, prev_input_scopes); - input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), - client->GetTextInputMode()); - if (input_scopes_ != prev_input_scopes && remote_delegate_) { - remote_delegate_->OnTextInputClientUpdated( - input_scopes_, composition_character_bounds_); - } - } - - void OnCaretBoundsChanged(const TextInputClient* client) override { - if (!text_input_client_ || text_input_client_ != client) - return; - std::vector<gfx::Rect> prev_rects; - std::swap(composition_character_bounds_, prev_rects); - composition_character_bounds_ = GetCompositionCharacterBounds(client); - if (composition_character_bounds_ != prev_rects && remote_delegate_) { - remote_delegate_->OnTextInputClientUpdated( - input_scopes_, composition_character_bounds_); - } - } - - void CancelComposition(const TextInputClient* client) override { - if (CanSendRemoteNotification(client)) - remote_delegate_->CancelComposition(); - } - - void OnInputLocaleChanged() override {} - - std::string GetInputLocale() override { - const LCID locale_id = MAKELCID(langid_, SORT_DEFAULT); - std::string language = - GetLocaleString(locale_id, LOCALE_SISO639LANGNAME); - if (SUBLANGID(langid_) == SUBLANG_NEUTRAL || language.empty()) - return language; - const std::string& region = - GetLocaleString(locale_id, LOCALE_SISO3166CTRYNAME); - if (region.empty()) - return language; - return language.append(1, '-').append(region); - } - - TextInputType GetTextInputType() const override { - return text_input_client_ ? text_input_client_->GetTextInputType() - : TEXT_INPUT_TYPE_NONE; - } - - TextInputMode GetTextInputMode() const override { - return text_input_client_ ? text_input_client_->GetTextInputMode() - : TEXT_INPUT_MODE_DEFAULT; - } - - int GetTextInputFlags() const override { - return text_input_client_ ? text_input_client_->GetTextInputFlags() - : 0; - } - - bool CanComposeInline() const override { - return text_input_client_ ? text_input_client_->CanComposeInline() : true; - } - - bool IsCandidatePopupOpen() const override { - return is_candidate_popup_open_; - } - - void ShowImeIfNeeded() override {} - - void AddObserver(InputMethodObserver* observer) override { - observer_list_.AddObserver(observer); - } - - void RemoveObserver(InputMethodObserver* observer) override { - observer_list_.RemoveObserver(observer); - } - - // Overridden from RemoteInputMethodPrivateWin: - void SetRemoteDelegate( - internal::RemoteInputMethodDelegateWin* delegate) override { - remote_delegate_ = delegate; - - // Sync initial state. - if (remote_delegate_) { - remote_delegate_->OnTextInputClientUpdated( - input_scopes_, composition_character_bounds_); - } - } - - void OnCandidatePopupChanged(bool visible) override { - is_candidate_popup_open_ = visible; - } - - void OnInputSourceChanged(LANGID langid, bool /*is_ime*/) override { - // Note: Currently |is_ime| is not utilized yet. - const bool changed = (langid_ != langid); - langid_ = langid; - if (changed && GetTextInputClient()) - GetTextInputClient()->OnInputMethodChanged(); - } - - void OnCompositionChanged(const CompositionText& composition_text) override { - if (!text_input_client_) - return; - text_input_client_->SetCompositionText(composition_text); - } - - void OnTextCommitted(const base::string16& text) override { - if (!text_input_client_) - return; - if (text_input_client_->GetTextInputType() == TEXT_INPUT_TYPE_NONE) { - // According to the comment in text_input_client.h, - // TextInputClient::InsertText should never be called when the - // text input type is TEXT_INPUT_TYPE_NONE. - - for (size_t i = 0; i < text.size(); ++i) { - ui::KeyEvent char_event(text[i], static_cast<ui::KeyboardCode>(text[i]), - ui::EF_NONE); - text_input_client_->InsertChar(char_event); - } - return; - } - text_input_client_->InsertText(text); - } - - bool CanSendRemoteNotification( - const TextInputClient* text_input_client) const { - return text_input_client_ && - text_input_client_ == text_input_client && - remote_delegate_; - } - - base::ObserverList<InputMethodObserver> observer_list_; - - internal::InputMethodDelegate* delegate_; - internal::RemoteInputMethodDelegateWin* remote_delegate_; - - TextInputClient* text_input_client_; - std::vector<int32_t> input_scopes_; - std::vector<gfx::Rect> composition_character_bounds_; - bool is_candidate_popup_open_; - bool is_ime_; - LANGID langid_; - - DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin); -}; - -} // namespace - -bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget) { - // If the remote input method is already registered then don't do it again. - if (ui::g_public_interface_ && ui::g_private_interface_) - return false; - - DWORD process_id = 0; - if (GetWindowThreadProcessId(widget, &process_id) == 0) - return false; - base::win::ScopedHandle process_handle(::OpenProcess( - PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id)); - if (!process_handle.IsValid()) - return false; - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kViewerConnect); -} - -RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {} - -scoped_ptr<InputMethod> CreateRemoteInputMethodWin( - internal::InputMethodDelegate* delegate) { - return make_scoped_ptr(new RemoteInputMethodWin(delegate)); -} - -// static -RemoteInputMethodPrivateWin* RemoteInputMethodPrivateWin::Get( - InputMethod* input_method) { - return GetPrivate(input_method); -} - -} // namespace ui diff --git a/chromium/ui/base/ime/remote_input_method_win.h b/chromium/ui/base/ime/remote_input_method_win.h deleted file mode 100644 index a1725b32dd3..00000000000 --- a/chromium/ui/base/ime/remote_input_method_win.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_IME_REMOTE_INPUT_METHOD_WIN_H_ -#define UI_BASE_IME_REMOTE_INPUT_METHOD_WIN_H_ - -#include <Windows.h> - -#include <string> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string16.h" -#include "ui/base/ime/ui_base_ime_export.h" -#include "ui/gfx/native_widget_types.h" - -namespace ui { -namespace internal { -class InputMethodDelegate; -class RemoteInputMethodDelegateWin; -} // namespace internal - -class InputMethod; -struct CompositionText; - -// RemoteInputMethodWin is a special implementation of ui::InputMethod that -// works as a proxy of an IME handler running in the metro_driver process. -// RemoteInputMethodWin works as follows. -// - Any action to RemoteInputMethodWin should be delegated to the -// metro_driver process via RemoteInputMethodDelegateWin. -// - Data retrieval from RemoteInputMethodPrivateWin is implemented with -// data cache. Whenever the IME state in the metro_driver process is changed, -// RemoteWindowTreeHostWin, which receives IPCs from metro_driver process, -// will call RemoteInputMethodPrivateWin::OnCandidatePopupChanged and/or -// RemoteInputMethodPrivateWin::OnInputSourceChanged accordingly so that -// the state cache should be updated. -// - Some IPC messages that represent actions to TextInputClient should be -// delegated to RemoteInputMethodPrivateWin so that RemoteInputMethodWin can -// work as a real proxy. - -// Returns true if |widget| requires RemoteInputMethodWin. -bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget); - -// Returns the public interface of RemoteInputMethodWin. -// Caveats: Currently only one instance of RemoteInputMethodWin is able to run -// at the same time. -UI_BASE_IME_EXPORT scoped_ptr<InputMethod> CreateRemoteInputMethodWin( - internal::InputMethodDelegate* delegate); - -// Private interface of RemoteInputMethodWin. -class UI_BASE_IME_EXPORT RemoteInputMethodPrivateWin { - public: - RemoteInputMethodPrivateWin(); - - // Returns the private interface of RemoteInputMethodWin when and only when - // |input_method| is instanciated via CreateRemoteInputMethodWin. Caller does - // not take the ownership of the returned object. - // As you might notice, this is yet another reinplementation of dynamic_cast - // or IUnknown::QueryInterface. - static RemoteInputMethodPrivateWin* Get(InputMethod* input_method); - - // Installs RemoteInputMethodDelegateWin delegate. Set NULL to |delegate| to - // unregister. - virtual void SetRemoteDelegate( - internal::RemoteInputMethodDelegateWin* delegate) = 0; - - // Updates internal cache so that subsequent calls of - // RemoteInputMethodWin::IsCandidatePopupOpen can return the correct value - // based on remote IME activities in the metro_driver process. - virtual void OnCandidatePopupChanged(bool visible) = 0; - - // Updates internal cache so that subsequent calls of - // RemoteInputMethodWin::GetInputLocale can return the correct values based on - // remote IME activities in the metro_driver process. - virtual void OnInputSourceChanged(LANGID langid, bool is_ime) = 0; - - // Handles composition-update events occurred in the metro_driver process. - // Caveats: This method is designed to be used only with - // metro_driver::TextService. In other words, there is no garantee that this - // method works a wrapper to call ui::TextInputClient::SetCompositionText. - virtual void OnCompositionChanged( - const CompositionText& composition_text) = 0; - - // Handles text-commit events occurred in the metro_driver process. - // Caveats: This method is designed to be used only with - // metro_driver::TextService. In other words, there is no garantee that this - // method works a wrapper to call ui::TextInputClient::InsertText. In fact, - // this method may call ui::TextInputClient::InsertChar when the text input - // type of the focused text input client is TEXT_INPUT_TYPE_NONE. - virtual void OnTextCommitted(const base::string16& text) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodPrivateWin); -}; - -} // namespace ui - -#endif // UI_BASE_IME_REMOTE_INPUT_METHOD_WIN_H_ diff --git a/chromium/ui/base/ime/remote_input_method_win_unittest.cc b/chromium/ui/base/ime/remote_input_method_win_unittest.cc deleted file mode 100644 index 2d15919d938..00000000000 --- a/chromium/ui/base/ime/remote_input_method_win_unittest.cc +++ /dev/null @@ -1,831 +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 "ui/base/ime/remote_input_method_win.h" - -#include <InputScope.h> -#include <stddef.h> -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/scoped_observer.h" -#include "base/strings/string16.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/ime/composition_text.h" -#include "ui/base/ime/dummy_text_input_client.h" -#include "ui/base/ime/input_method.h" -#include "ui/base/ime/input_method_delegate.h" -#include "ui/base/ime/input_method_observer.h" -#include "ui/base/ime/remote_input_method_delegate_win.h" -#include "ui/events/event.h" - -namespace ui { -namespace { - -class MockTextInputClient : public DummyTextInputClient { - public: - MockTextInputClient() - : text_input_type_(TEXT_INPUT_TYPE_NONE), - text_input_mode_(TEXT_INPUT_MODE_DEFAULT), - call_count_set_composition_text_(0), - call_count_insert_char_(0), - call_count_insert_text_(0), - emulate_pepper_flash_(false) { - } - - size_t call_count_set_composition_text() const { - return call_count_set_composition_text_; - } - const base::string16& inserted_text() const { - return inserted_text_; - } - size_t call_count_insert_char() const { - return call_count_insert_char_; - } - size_t call_count_insert_text() const { - return call_count_insert_text_; - } - void Reset() { - text_input_type_ = TEXT_INPUT_TYPE_NONE; - text_input_mode_ = TEXT_INPUT_MODE_DEFAULT; - call_count_set_composition_text_ = 0; - inserted_text_.clear(); - call_count_insert_char_ = 0; - call_count_insert_text_ = 0; - caret_bounds_ = gfx::Rect(); - composition_character_bounds_.clear(); - emulate_pepper_flash_ = false; - } - void set_text_input_type(ui::TextInputType type) { - text_input_type_ = type; - } - void set_text_input_mode(ui::TextInputMode mode) { - text_input_mode_ = mode; - } - void set_caret_bounds(const gfx::Rect& caret_bounds) { - caret_bounds_ = caret_bounds; - } - void set_composition_character_bounds( - const std::vector<gfx::Rect>& composition_character_bounds) { - composition_character_bounds_ = composition_character_bounds; - } - void set_emulate_pepper_flash(bool enabled) { - emulate_pepper_flash_ = enabled; - } - - private: - // Overriden from DummyTextInputClient. - void SetCompositionText(const ui::CompositionText& composition) override { - ++call_count_set_composition_text_; - } - void InsertChar(const ui::KeyEvent& event) override { - inserted_text_.append(1, event.GetCharacter()); - ++call_count_insert_char_; - } - void InsertText(const base::string16& text) override { - inserted_text_.append(text); - ++call_count_insert_text_; - } - ui::TextInputType GetTextInputType() const override { - return text_input_type_; - } - ui::TextInputMode GetTextInputMode() const override { - return text_input_mode_; - } - gfx::Rect GetCaretBounds() const override { return caret_bounds_; } - bool GetCompositionCharacterBounds(uint32_t index, - gfx::Rect* rect) const override { - // Emulate the situation of crbug.com/328237. - if (emulate_pepper_flash_) - return false; - if (!rect || composition_character_bounds_.size() <= index) - return false; - *rect = composition_character_bounds_[index]; - return true; - } - bool HasCompositionText() const override { - return !composition_character_bounds_.empty(); - } - bool GetCompositionTextRange(gfx::Range* range) const override { - if (composition_character_bounds_.empty()) - return false; - *range = gfx::Range(0, composition_character_bounds_.size()); - return true; - } - - ui::TextInputType text_input_type_; - ui::TextInputMode text_input_mode_; - gfx::Rect caret_bounds_; - std::vector<gfx::Rect> composition_character_bounds_; - base::string16 inserted_text_; - size_t call_count_set_composition_text_; - size_t call_count_insert_char_; - size_t call_count_insert_text_; - bool emulate_pepper_flash_; - DISALLOW_COPY_AND_ASSIGN(MockTextInputClient); -}; - -class MockInputMethodDelegate : public internal::InputMethodDelegate { - public: - MockInputMethodDelegate() {} - - const std::vector<ui::KeyboardCode>& fabricated_key_events() const { - return fabricated_key_events_; - } - void Reset() { - fabricated_key_events_.clear(); - } - - private: - ui::EventDispatchDetails DispatchKeyEventPostIME( - ui::KeyEvent* event) override { - EXPECT_FALSE(event->HasNativeEvent()); - fabricated_key_events_.push_back(event->key_code()); - event->SetHandled(); - return ui::EventDispatchDetails(); - } - - std::vector<ui::KeyboardCode> fabricated_key_events_; - DISALLOW_COPY_AND_ASSIGN(MockInputMethodDelegate); -}; - -class MockRemoteInputMethodDelegateWin - : public internal::RemoteInputMethodDelegateWin { - public: - MockRemoteInputMethodDelegateWin() - : cancel_composition_called_(false), - text_input_client_updated_called_(false) { - } - - bool cancel_composition_called() const { - return cancel_composition_called_; - } - bool text_input_client_updated_called() const { - return text_input_client_updated_called_; - } - const std::vector<int32_t>& input_scopes() const { return input_scopes_; } - const std::vector<gfx::Rect>& composition_character_bounds() const { - return composition_character_bounds_; - } - void Reset() { - cancel_composition_called_ = false; - text_input_client_updated_called_ = false; - input_scopes_.clear(); - composition_character_bounds_.clear(); - } - - private: - void CancelComposition() override { cancel_composition_called_ = true; } - - void OnTextInputClientUpdated( - const std::vector<int32_t>& input_scopes, - const std::vector<gfx::Rect>& composition_character_bounds) override { - text_input_client_updated_called_ = true; - input_scopes_ = input_scopes; - composition_character_bounds_ = composition_character_bounds; - } - - bool cancel_composition_called_; - bool text_input_client_updated_called_; - std::vector<int32_t> input_scopes_; - std::vector<gfx::Rect> composition_character_bounds_; - DISALLOW_COPY_AND_ASSIGN(MockRemoteInputMethodDelegateWin); -}; - -class MockInputMethodObserver : public InputMethodObserver { - public: - MockInputMethodObserver() - : on_text_input_state_changed_(0), - on_input_method_destroyed_changed_(0) { - } - ~MockInputMethodObserver() override {} - void Reset() { - on_text_input_state_changed_ = 0; - on_input_method_destroyed_changed_ = 0; - } - size_t on_text_input_state_changed() const { - return on_text_input_state_changed_; - } - size_t on_input_method_destroyed_changed() const { - return on_input_method_destroyed_changed_; - } - - private: - // Overriden from InputMethodObserver. - void OnTextInputTypeChanged(const TextInputClient* client) override {} - void OnFocus() override {} - void OnBlur() override {} - void OnCaretBoundsChanged(const TextInputClient* client) override {} - void OnTextInputStateChanged(const TextInputClient* client) override { - ++on_text_input_state_changed_; - } - void OnInputMethodDestroyed(const InputMethod* client) override { - ++on_input_method_destroyed_changed_; - } - void OnShowImeIfNeeded() override {} - - size_t on_text_input_state_changed_; - size_t on_input_method_destroyed_changed_; - DISALLOW_COPY_AND_ASSIGN(MockInputMethodObserver); -}; - -typedef ScopedObserver<InputMethod, InputMethodObserver> - InputMethodScopedObserver; - -TEST(RemoteInputMethodWinTest, RemoteInputMethodPrivateWin) { - InputMethod* other_ptr = static_cast<InputMethod*>(NULL) + 1; - - // Use typed NULL to make EXPECT_NE happy until nullptr becomes available. - RemoteInputMethodPrivateWin* kNull = - static_cast<RemoteInputMethodPrivateWin*>(NULL); - EXPECT_EQ(kNull, RemoteInputMethodPrivateWin::Get(other_ptr)); - - MockInputMethodDelegate delegate_; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - EXPECT_NE(kNull, RemoteInputMethodPrivateWin::Get(input_method.get())); - - InputMethod* dangling_ptr = input_method.get(); - input_method.reset(NULL); - EXPECT_EQ(kNull, RemoteInputMethodPrivateWin::Get(dangling_ptr)); -} - -TEST(RemoteInputMethodWinTest, OnInputSourceChanged) { - MockInputMethodDelegate delegate_; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - - private_ptr->OnInputSourceChanged( - MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), true); - EXPECT_EQ("ja-JP", input_method->GetInputLocale()); - - private_ptr->OnInputSourceChanged( - MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_QATAR), true); - EXPECT_EQ("ar-QA", input_method->GetInputLocale()); -} - -TEST(RemoteInputMethodWinTest, OnCandidatePopupChanged) { - MockInputMethodDelegate delegate_; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - - // Initial value - EXPECT_FALSE(input_method->IsCandidatePopupOpen()); - - // RemoteInputMethodWin::OnCandidatePopupChanged can be called even when the - // focused text input client is NULL. - ASSERT_TRUE(input_method->GetTextInputClient() == NULL); - private_ptr->OnCandidatePopupChanged(false); - private_ptr->OnCandidatePopupChanged(true); - - MockTextInputClient mock_text_input_client; - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - mock_text_input_client.Reset(); - - private_ptr->OnCandidatePopupChanged(true); - EXPECT_TRUE(input_method->IsCandidatePopupOpen()); - - private_ptr->OnCandidatePopupChanged(false); - EXPECT_FALSE(input_method->IsCandidatePopupOpen()); -} - -TEST(RemoteInputMethodWinTest, CancelComposition) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - // This must not cause a crash. - input_method->CancelComposition(&mock_text_input_client); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - input_method->CancelComposition(&mock_text_input_client); - EXPECT_FALSE(mock_remote_delegate.cancel_composition_called()); - - input_method->SetFocusedTextInputClient(&mock_text_input_client); - input_method->CancelComposition(&mock_text_input_client); - EXPECT_TRUE(mock_remote_delegate.cancel_composition_called()); -} - -TEST(RemoteInputMethodWinTest, SetFocusedTextInputClient) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20)); - mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL); - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // Initial state must be synced. - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); - EXPECT_EQ(gfx::Rect(10, 0, 10, 20), - mock_remote_delegate.composition_character_bounds()[0]); - ASSERT_EQ(1u, mock_remote_delegate.input_scopes().size()); - EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]); - - // State must be cleared by SetFocusedTextInputClient(NULL). - mock_remote_delegate.Reset(); - input_method->SetFocusedTextInputClient(NULL); - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - EXPECT_TRUE(mock_remote_delegate.composition_character_bounds().empty()); - EXPECT_TRUE(mock_remote_delegate.input_scopes().empty()); -} - -TEST(RemoteInputMethodWinTest, DetachTextInputClient) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20)); - mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL); - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // Initial state must be synced. - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); - EXPECT_EQ(gfx::Rect(10, 0, 10, 20), - mock_remote_delegate.composition_character_bounds()[0]); - ASSERT_EQ(1u, mock_remote_delegate.input_scopes().size()); - EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]); - - // State must be cleared by DetachTextInputClient - mock_remote_delegate.Reset(); - input_method->DetachTextInputClient(&mock_text_input_client); - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - EXPECT_TRUE(mock_remote_delegate.composition_character_bounds().empty()); - EXPECT_TRUE(mock_remote_delegate.input_scopes().empty()); -} - -TEST(RemoteInputMethodWinTest, OnCaretBoundsChanged) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - // This must not cause a crash. - input_method->OnCaretBoundsChanged(&mock_text_input_client); - - mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20)); - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // Initial state must be synced. - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); - EXPECT_EQ(gfx::Rect(10, 0, 10, 20), - mock_remote_delegate.composition_character_bounds()[0]); - - // Redundant OnCaretBoundsChanged must be ignored. - mock_remote_delegate.Reset(); - input_method->OnCaretBoundsChanged(&mock_text_input_client); - EXPECT_FALSE(mock_remote_delegate.text_input_client_updated_called()); - - // Check OnCaretBoundsChanged is handled. (w/o composition) - mock_remote_delegate.Reset(); - mock_text_input_client.Reset(); - mock_text_input_client.set_caret_bounds(gfx::Rect(10, 20, 30, 40)); - input_method->OnCaretBoundsChanged(&mock_text_input_client); - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); - EXPECT_EQ(gfx::Rect(10, 20, 30, 40), - mock_remote_delegate.composition_character_bounds()[0]); - - // Check OnCaretBoundsChanged is handled. (w/ composition) - { - mock_remote_delegate.Reset(); - mock_text_input_client.Reset(); - - std::vector<gfx::Rect> bounds; - bounds.push_back(gfx::Rect(10, 20, 30, 40)); - bounds.push_back(gfx::Rect(40, 30, 20, 10)); - mock_text_input_client.set_composition_character_bounds(bounds); - input_method->OnCaretBoundsChanged(&mock_text_input_client); - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - EXPECT_EQ(bounds, mock_remote_delegate.composition_character_bounds()); - } -} - -// Test case against crbug.com/328237. -TEST(RemoteInputMethodWinTest, OnCaretBoundsChangedForPepperFlash) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - mock_remote_delegate.Reset(); - mock_text_input_client.Reset(); - mock_text_input_client.set_emulate_pepper_flash(true); - - std::vector<gfx::Rect> caret_bounds; - caret_bounds.push_back(gfx::Rect(5, 15, 25, 35)); - mock_text_input_client.set_caret_bounds(caret_bounds[0]); - - std::vector<gfx::Rect> composition_bounds; - composition_bounds.push_back(gfx::Rect(10, 20, 30, 40)); - composition_bounds.push_back(gfx::Rect(40, 30, 20, 10)); - mock_text_input_client.set_composition_character_bounds(composition_bounds); - input_method->OnCaretBoundsChanged(&mock_text_input_client); - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - // The caret bounds must be used when - // TextInputClient::GetCompositionCharacterBounds failed. - EXPECT_EQ(caret_bounds, mock_remote_delegate.composition_character_bounds()); -} - -TEST(RemoteInputMethodWinTest, OnTextInputTypeChanged) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - // This must not cause a crash. - input_method->OnCaretBoundsChanged(&mock_text_input_client); - - mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL); - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // Initial state must be synced. - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - ASSERT_EQ(1u, mock_remote_delegate.input_scopes().size()); - EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]); - - // Check TEXT_INPUT_TYPE_NONE is handled. - mock_remote_delegate.Reset(); - mock_text_input_client.Reset(); - mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_NONE); - mock_text_input_client.set_text_input_mode(ui::TEXT_INPUT_MODE_KATAKANA); - input_method->OnTextInputTypeChanged(&mock_text_input_client); - EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); - EXPECT_TRUE(mock_remote_delegate.input_scopes().empty()); - - // Redundant OnTextInputTypeChanged must be ignored. - mock_remote_delegate.Reset(); - input_method->OnTextInputTypeChanged(&mock_text_input_client); - EXPECT_FALSE(mock_remote_delegate.text_input_client_updated_called()); - - mock_remote_delegate.Reset(); - mock_text_input_client.Reset(); - mock_text_input_client.set_caret_bounds(gfx::Rect(10, 20, 30, 40)); - input_method->OnCaretBoundsChanged(&mock_text_input_client); -} - -TEST(RemoteInputMethodWinTest, DispatchKeyEvent_NativeKeyEvent) { - // Basically RemoteInputMethodWin does not handle native keydown event. - - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - const MSG wm_keydown = { NULL, WM_KEYDOWN, ui::VKEY_A }; - ui::KeyEvent new_keydown(wm_keydown); - ui::KeyEvent native_keydown(new_keydown); - - // This must not cause a crash. - input_method->DispatchKeyEvent(&native_keydown); - EXPECT_FALSE(native_keydown.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // TextInputClient is not focused yet here. - native_keydown = new_keydown; - input_method->DispatchKeyEvent(&native_keydown); - EXPECT_FALSE(native_keydown.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - // TextInputClient is now focused here. - native_keydown = new_keydown; - input_method->DispatchKeyEvent(&native_keydown); - EXPECT_FALSE(native_keydown.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); -} - -TEST(RemoteInputMethodWinTest, DispatchKeyEvent_NativeCharEvent) { - // RemoteInputMethodWin handles native char event if possible. - - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - const MSG wm_char = { NULL, WM_CHAR, 'A', 0 }; - ui::KeyEvent new_char(wm_char); - ui::KeyEvent native_char(new_char); - - // This must not cause a crash. - input_method->DispatchKeyEvent(&native_char); - EXPECT_FALSE(native_char.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // TextInputClient is not focused yet here. - native_char = new_char; - input_method->DispatchKeyEvent(&native_char); - EXPECT_FALSE(native_char.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - // TextInputClient is now focused here. - native_char = new_char; - input_method->DispatchKeyEvent(&native_char); - EXPECT_TRUE(native_char.handled()); - EXPECT_EQ(L"A", mock_text_input_client.inserted_text()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); -} - -TEST(RemoteInputMethodWinTest, DispatchKeyEvent_FabricatedKeyDown) { - // Fabricated non-char event will be delegated to - // InputMethodDelegate::DispatchFabricatedKeyEventPostIME as long as the - // delegate is installed. - - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - ui::KeyEvent new_keydown(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE); - new_keydown.set_character(L'A'); - ui::KeyEvent fabricated_keydown(new_keydown); - - // This must not cause a crash. - input_method->DispatchKeyEvent(&fabricated_keydown); - EXPECT_TRUE(fabricated_keydown.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - ASSERT_EQ(1u, delegate_.fabricated_key_events().size()); - EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]); - delegate_.Reset(); - mock_text_input_client.Reset(); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // TextInputClient is not focused yet here. - fabricated_keydown = new_keydown; - input_method->DispatchKeyEvent(&fabricated_keydown); - EXPECT_TRUE(fabricated_keydown.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - ASSERT_EQ(1u, delegate_.fabricated_key_events().size()); - EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]); - delegate_.Reset(); - mock_text_input_client.Reset(); - - input_method->SetFocusedTextInputClient(&mock_text_input_client); - // TextInputClient is now focused here. - fabricated_keydown = new_keydown; - input_method->DispatchKeyEvent(&fabricated_keydown); - EXPECT_TRUE(fabricated_keydown.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - ASSERT_EQ(1u, delegate_.fabricated_key_events().size()); - EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]); - delegate_.Reset(); - mock_text_input_client.Reset(); - - input_method->SetDelegate(NULL); - // RemoteInputMethodDelegateWin is no longer set here. - fabricated_keydown = new_keydown; - input_method->DispatchKeyEvent(&fabricated_keydown); - EXPECT_FALSE(fabricated_keydown.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); -} - -TEST(RemoteInputMethodWinTest, DispatchKeyEvent_FabricatedChar) { - // Note: RemoteInputMethodWin::DispatchKeyEvent should always return true - // for fabricated character events. - - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - ui::KeyEvent new_char(L'A', ui::VKEY_A, ui::EF_NONE); - ui::KeyEvent fabricated_char(new_char); - - // This must not cause a crash. - input_method->DispatchKeyEvent(&fabricated_char); - EXPECT_TRUE(fabricated_char.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - // TextInputClient is not focused yet here. - fabricated_char = new_char; - input_method->DispatchKeyEvent(&fabricated_char); - EXPECT_TRUE(fabricated_char.handled()); - EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - // TextInputClient is now focused here. - fabricated_char = new_char; - input_method->DispatchKeyEvent(&fabricated_char); - EXPECT_TRUE(fabricated_char.handled()); - EXPECT_EQ(L"A", mock_text_input_client.inserted_text()); - EXPECT_TRUE(delegate_.fabricated_key_events().empty()); - delegate_.Reset(); - mock_text_input_client.Reset(); -} - -TEST(RemoteInputMethodWinTest, OnCompositionChanged) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - CompositionText composition_text; - - // TextInputClient is not focused yet here. - - private_ptr->OnCompositionChanged(composition_text); - EXPECT_EQ(0u, mock_text_input_client.call_count_set_composition_text()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - // TextInputClient is now focused here. - - private_ptr->OnCompositionChanged(composition_text); - EXPECT_EQ(1u, mock_text_input_client.call_count_set_composition_text()); - delegate_.Reset(); - mock_text_input_client.Reset(); -} - -TEST(RemoteInputMethodWinTest, OnTextCommitted) { - MockInputMethodDelegate delegate_; - MockTextInputClient mock_text_input_client; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - - RemoteInputMethodPrivateWin* private_ptr = - RemoteInputMethodPrivateWin::Get(input_method.get()); - ASSERT_TRUE(private_ptr != NULL); - MockRemoteInputMethodDelegateWin mock_remote_delegate; - private_ptr->SetRemoteDelegate(&mock_remote_delegate); - - base::string16 committed_text = L"Hello"; - - // TextInputClient is not focused yet here. - - mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_TEXT); - private_ptr->OnTextCommitted(committed_text); - EXPECT_EQ(0u, mock_text_input_client.call_count_insert_char()); - EXPECT_EQ(0u, mock_text_input_client.call_count_insert_text()); - EXPECT_EQ(L"", mock_text_input_client.inserted_text()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - input_method->SetFocusedTextInputClient(&mock_text_input_client); - - // TextInputClient is now focused here. - - mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_TEXT); - private_ptr->OnTextCommitted(committed_text); - EXPECT_EQ(0u, mock_text_input_client.call_count_insert_char()); - EXPECT_EQ(1u, mock_text_input_client.call_count_insert_text()); - EXPECT_EQ(committed_text, mock_text_input_client.inserted_text()); - delegate_.Reset(); - mock_text_input_client.Reset(); - - // When TextInputType is TEXT_INPUT_TYPE_NONE, TextInputClient::InsertText - // should not be used. - mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_NONE); - private_ptr->OnTextCommitted(committed_text); - EXPECT_EQ(committed_text.size(), - mock_text_input_client.call_count_insert_char()); - EXPECT_EQ(0u, mock_text_input_client.call_count_insert_text()); - EXPECT_EQ(committed_text, mock_text_input_client.inserted_text()); - delegate_.Reset(); - mock_text_input_client.Reset(); -} - -TEST(RemoteInputMethodWinTest, OnTextInputStateChanged_Observer) { - DummyTextInputClient text_input_client; - DummyTextInputClient text_input_client_the_other; - - MockInputMethodObserver input_method_observer; - MockInputMethodDelegate delegate_; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - InputMethodScopedObserver scoped_observer(&input_method_observer); - scoped_observer.Add(input_method.get()); - - input_method->SetFocusedTextInputClient(&text_input_client); - ASSERT_EQ(&text_input_client, input_method->GetTextInputClient()); - EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed()); - input_method_observer.Reset(); - - input_method->SetFocusedTextInputClient(&text_input_client); - ASSERT_EQ(&text_input_client, input_method->GetTextInputClient()); - EXPECT_EQ(0u, input_method_observer.on_text_input_state_changed()); - input_method_observer.Reset(); - - input_method->SetFocusedTextInputClient(&text_input_client_the_other); - ASSERT_EQ(&text_input_client_the_other, input_method->GetTextInputClient()); - EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed()); - input_method_observer.Reset(); - - input_method->DetachTextInputClient(&text_input_client_the_other); - ASSERT_TRUE(input_method->GetTextInputClient() == NULL); - EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed()); - input_method_observer.Reset(); -} - -TEST(RemoteInputMethodWinTest, OnInputMethodDestroyed_Observer) { - DummyTextInputClient text_input_client; - DummyTextInputClient text_input_client_the_other; - - MockInputMethodObserver input_method_observer; - InputMethodScopedObserver scoped_observer(&input_method_observer); - - MockInputMethodDelegate delegate_; - scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); - input_method->AddObserver(&input_method_observer); - - EXPECT_EQ(0u, input_method_observer.on_input_method_destroyed_changed()); - input_method.reset(); - EXPECT_EQ(1u, input_method_observer.on_input_method_destroyed_changed()); -} - -} // namespace -} // namespace ui diff --git a/chromium/ui/base/ime/ui_base_ime.gyp b/chromium/ui/base/ime/ui_base_ime.gyp index 22c3e720148..ed873ae0504 100644 --- a/chromium/ui/base/ime/ui_base_ime.gyp +++ b/chromium/ui/base/ime/ui_base_ime.gyp @@ -7,6 +7,16 @@ 'chromium_code': 1, }, 'targets': [ + { + # GN version: //ui/base/ime:text_input_types + 'target_name': 'text_input_types', + 'type': 'none', + 'sources' : [ + 'text_input_flags.h', + 'text_input_mode.h', + 'text_input_type.h', + ], + }, { # GN version: //ui/base/ime 'target_name': 'ui_base_ime', @@ -25,6 +35,7 @@ '../../gfx/gfx.gyp:gfx', '../../gfx/gfx.gyp:gfx_geometry', '../ui_base.gyp:ui_base', + ':text_input_types', ], 'defines': [ 'UI_BASE_IME_IMPLEMENTATION', @@ -56,8 +67,6 @@ 'chromeos/input_method_descriptor.h', 'chromeos/input_method_manager.cc', 'chromeos/input_method_manager.h', - 'chromeos/input_method_whitelist.cc', - 'chromeos/input_method_whitelist.h', 'chromeos/mock_component_extension_ime_manager_delegate.cc', 'chromeos/mock_component_extension_ime_manager_delegate.h', 'chromeos/mock_ime_candidate_window_handler.cc', @@ -73,9 +82,7 @@ 'composition_underline.h', 'ime_bridge.cc', 'ime_bridge.h', - 'ime_engine_handler_interface.cc', 'ime_engine_handler_interface.h', - 'ime_engine_observer.h', 'ime_input_context_handler_interface.h', 'infolist_entry.cc', 'infolist_entry.h', @@ -107,12 +114,8 @@ 'linux/linux_input_method_context_factory.h', 'mock_input_method.cc', 'mock_input_method.h', - 'remote_input_method_delegate_win.h', - 'remote_input_method_win.cc', - 'remote_input_method_win.h', 'text_input_client.cc', 'text_input_client.h', - 'text_input_type.h', 'ui_base_ime_export.h', 'win/imm32_manager.cc', 'win/imm32_manager.h', diff --git a/chromium/ui/base/ime/win/imm32_manager.cc b/chromium/ui/base/ime/win/imm32_manager.cc index 853bf3ff26a..9f03a2a8651 100644 --- a/chromium/ui/base/ime/win/imm32_manager.cc +++ b/chromium/ui/base/ime/win/imm32_manager.cc @@ -15,11 +15,6 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/base/ime/composition_text.h" -// "imm32.lib" is required by IMM32 APIs used in this file. -// NOTE(hbono): To comply with a comment from Darin, I have added -// this #pragma directive instead of adding "imm32.lib" to a project file. -#pragma comment(lib, "imm32.lib") - // Following code requires wchar_t to be same as char16. It should always be // true on Windows. static_assert(sizeof(wchar_t) == sizeof(base::char16), diff --git a/chromium/ui/base/ios/cru_context_menu_controller.h b/chromium/ui/base/ios/cru_context_menu_controller.h index cb966fa00cd..f656c4af154 100644 --- a/chromium/ui/base/ios/cru_context_menu_controller.h +++ b/chromium/ui/base/ios/cru_context_menu_controller.h @@ -6,6 +6,8 @@ #import <UIKit/UIKit.h> +#include "base/ios/block_types.h" + @class CRUContextMenuHolder; // Abstracts displaying context menus for all device form factors, given a @@ -25,6 +27,10 @@ atPoint:(CGPoint)localPoint inView:(UIView*)view; +// Dismisses displayed context menu. +- (void)dismissAnimated:(BOOL)animated + completionHandler:(ProceduralBlock)completionHandler; + @end #endif // UI_BASE_IOS_CRU_CONTEXT_MENU_CONTROLLER_H_ diff --git a/chromium/ui/base/ios/cru_context_menu_controller.mm b/chromium/ui/base/ios/cru_context_menu_controller.mm index f615c017b06..d052c99070a 100644 --- a/chromium/ui/base/ios/cru_context_menu_controller.mm +++ b/chromium/ui/base/ios/cru_context_menu_controller.mm @@ -6,34 +6,14 @@ #include <algorithm> -#include "base/ios/ios_util.h" #include "base/ios/weak_nsobject.h" #include "base/logging.h" #import "base/mac/scoped_nsobject.h" #include "ui/base/device_form_factor.h" #import "ui/base/ios/cru_context_menu_holder.h" #include "ui/base/l10n/l10n_util.h" -#import "ui/gfx/ios/NSString+CrStringDrawing.h" #include "ui/strings/grit/ui_strings.h" -namespace { - -// Returns the screen's height in points. -CGFloat GetScreenHeight() { - DCHECK(!base::ios::IsRunningOnIOS8OrLater()); - switch ([[UIApplication sharedApplication] statusBarOrientation]) { - case UIInterfaceOrientationLandscapeLeft: - case UIInterfaceOrientationLandscapeRight: - return CGRectGetWidth([[UIScreen mainScreen] bounds]); - case UIInterfaceOrientationPortraitUpsideDown: - case UIInterfaceOrientationPortrait: - case UIInterfaceOrientationUnknown: - return CGRectGetHeight([[UIScreen mainScreen] bounds]); - } -} - -} // namespace - // Abstracts system implementation of popovers and action sheets. @protocol CRUContextMenuControllerImpl<NSObject> @@ -44,21 +24,18 @@ CGFloat GetScreenHeight() { - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder atPoint:(CGPoint)localPoint inView:(UIView*)view; -@end -// Backs up CRUContextMenuController on iOS 7 by using UIActionSheet. -@interface CRUActionSheetController - : NSObject<CRUContextMenuControllerImpl, UIActionSheetDelegate> { - // The action sheet used to display the UI. - base::scoped_nsobject<UIActionSheet> _sheet; - // Holds all the titles and actions for the menu. - base::scoped_nsobject<CRUContextMenuHolder> _menuHolder; -} +// Dismisses displayed context menu. +- (void)dismissAnimated:(BOOL)animated + completionHandler:(ProceduralBlock)completionHandler; + @end -// Backs up CRUContextMenuController on iOS 8 and higher by using -// UIAlertController. -@interface CRUAlertController : NSObject<CRUContextMenuControllerImpl> +// Backs up CRUContextMenuController by using UIAlertController. +@interface CRUAlertController : NSObject<CRUContextMenuControllerImpl> { + // Weak underlying UIAlertController. + base::WeakNSObject<UIAlertController> _alert; +} // Redefined to readwrite. @property(nonatomic, readwrite, getter=isVisible) BOOL visible; @end @@ -76,15 +53,16 @@ CGFloat GetScreenHeight() { - (instancetype)init { self = [super init]; if (self) { - if (base::ios::IsRunningOnIOS8OrLater()) { - _impl.reset([[CRUAlertController alloc] init]); - } else { - _impl.reset([[CRUActionSheetController alloc] init]); - } + _impl.reset([[CRUAlertController alloc] init]); } return self; } +- (void)dealloc { + [_impl dismissAnimated:NO completionHandler:nil]; + [super dealloc]; +} + - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder atPoint:(CGPoint)point inView:(UIView*)view { @@ -96,134 +74,13 @@ CGFloat GetScreenHeight() { [_impl showWithHolder:menuHolder atPoint:point inView:view]; } -@end - -#pragma mark - iOS 7 - -@implementation CRUActionSheetController -@synthesize visible = _visible; - -- (void)dealloc { - if (_visible) { - // Context menu must be dismissed explicitly if it is still visible. - NSUInteger cancelButtonIndex = [_menuHolder itemCount]; - [_sheet dismissWithClickedButtonIndex:cancelButtonIndex animated:NO]; - } - [super dealloc]; -} - -- (void)showWithHolder:(CRUContextMenuHolder*)menuHolder - atPoint:(CGPoint)point - inView:(UIView*)view { - // If the content of UIActionSheet does not fit the screen then scrollbars - // are added to the menu items area. If that's the case, elide the title to - // avoid having scrollbars for menu items. - CGSize spaceAvailableForTitle = - [self sizeForTitleThatFitsMenuWithHolder:menuHolder - atPoint:point - inView:view]; - NSString* menuTitle = menuHolder.menuTitle; - if (menuTitle) { - // Show at least one line of text, even if that means the action sheet's - // items will need to scroll. - const CGFloat kMinimumVerticalSpace = 21; - spaceAvailableForTitle.height = - std::max(kMinimumVerticalSpace, spaceAvailableForTitle.height); - menuTitle = [menuTitle cr_stringByElidingToFitSize:spaceAvailableForTitle]; - } - - // Present UIActionSheet. - _sheet.reset( - [self newActionSheetWithHolder:menuHolder title:menuTitle delegate:self]); - [_sheet setCancelButtonIndex:menuHolder.itemCount]; - [_sheet showFromRect:CGRectMake(point.x, point.y, 1.0, 1.0) - inView:view - animated:YES]; - - _menuHolder.reset([menuHolder retain]); - _visible = YES; -} - -#pragma mark Implementation - -// Returns an approximation of the free space available for the title of an -// actionSheet filled with |menu| shown in |view| at |point|. -- (CGSize)sizeForTitleThatFitsMenuWithHolder:(CRUContextMenuHolder*)menuHolder - atPoint:(CGPoint)point - inView:(UIView*)view { - // Create a dummy UIActionSheet. - base::scoped_nsobject<UIActionSheet> dummySheet( - [self newActionSheetWithHolder:menuHolder title:nil delegate:nil]); - // Temporarily add the dummy UIActionSheet to |view|. - [dummySheet showFromRect:CGRectMake(point.x, point.y, 1.0, 1.0) - inView:view - animated:NO]; - // On iPad the actionsheet is positioned under or over |point| (as opposed - // to next to it) when the user clicks within approximately 200 points of - // respectively the top or bottom edge. This reduces the amount of vertical - // space available for the title, hence the large padding on ipad. - const CGFloat kPaddingiPad = 200; - const CGFloat kPaddingiPhone = 20; - BOOL isIPad = ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET; - const CGFloat padding = isIPad ? kPaddingiPad : kPaddingiPhone; - // A title uses the full width of the actionsheet and all the vertical - // space on the screen. - CGSize result = CGSizeMake( - CGRectGetWidth([dummySheet frame]), - GetScreenHeight() - CGRectGetHeight([dummySheet frame]) - padding); - [dummySheet dismissWithClickedButtonIndex:0 animated:NO]; - return result; -} - -// Returns an UIActionSheet. Callers responsible for releasing returned object. -- (UIActionSheet*)newActionSheetWithHolder:(CRUContextMenuHolder*)menuHolder - title:(NSString*)title - delegate:(id<UIActionSheetDelegate>)delegate { - UIActionSheet* sheet = [[UIActionSheet alloc] initWithTitle:title - delegate:delegate - cancelButtonTitle:nil - destructiveButtonTitle:nil - otherButtonTitles:nil]; - - for (NSString* itemTitle in menuHolder.itemTitles) { - [sheet addButtonWithTitle:itemTitle]; - } - [sheet addButtonWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)]; - return sheet; -} - -#pragma mark UIActionSheetDelegate - -// Called when the action sheet is dismissed in the modal context menu sheet. -// There is no way to dismiss the sheet without going through this method. Note -// that on iPad this method is called with the index of an nonexistent cancel -// button when the user taps outside the sheet. -- (void)actionSheet:(UIActionSheet*)actionSheet - didDismissWithButtonIndex:(NSInteger)buttonIndex { - NSUInteger unsignedButtonIndex = buttonIndex; - // Assumes "cancel" button is last in order. - if (unsignedButtonIndex < [_menuHolder itemCount]) - [_menuHolder performActionAtIndex:unsignedButtonIndex]; - _menuHolder.reset(); - _visible = NO; -} - -// Called when the user chooses a button in the modal context menu sheet. Note -// that on iPad this method is called with the index of an nonexistent cancel -// button when the user taps outside the sheet. -- (void)actionSheet:(UIActionSheet*)actionSheet - clickedButtonAtIndex:(NSInteger)buttonIndex { - // Some use cases (e.g. opening a new tab on handset) should not wait for the - // action sheet to animate away before executing the action. - if ([_menuHolder shouldDismissImmediatelyOnClickedAtIndex:buttonIndex]) { - [_sheet dismissWithClickedButtonIndex:buttonIndex animated:NO]; - } +- (void)dismissAnimated:(BOOL)animated + completionHandler:(ProceduralBlock)completionHandler { + [_impl dismissAnimated:animated completionHandler:completionHandler]; } @end -#pragma mark - iOS8 and higher - @implementation CRUAlertController @synthesize visible = _visible; @@ -283,6 +140,12 @@ CGFloat GetScreenHeight() { topController = topController.presentedViewController; [topController presentViewController:alert animated:YES completion:nil]; self.visible = YES; + _alert.reset(alert); +} + +- (void)dismissAnimated:(BOOL)animated + completionHandler:(ProceduralBlock)completionHandler { + [_alert dismissViewControllerAnimated:animated completion:completionHandler]; } @end diff --git a/chromium/ui/base/l10n/l10n_util.cc b/chromium/ui/base/l10n/l10n_util.cc index c3e850da221..8f262c511be 100644 --- a/chromium/ui/base/l10n/l10n_util.cc +++ b/chromium/ui/base/l10n/l10n_util.cc @@ -50,7 +50,9 @@ namespace { static const char* const kAcceptLanguageList[] = { "af", // Afrikaans "am", // Amharic + "an", // Aragonese "ar", // Arabic + "ast", // Asturian "az", // Azerbaijani "be", // Belarusian "bg", // Bulgarian @@ -74,6 +76,7 @@ static const char* const kAcceptLanguageList[] = { "en-AU", // English (Australia) "en-CA", // English (Canada) "en-GB", // English (UK) + "en-IN", // English (India) "en-NZ", // English (New Zealand) "en-US", // English (US) "en-ZA", // English (South Africa) @@ -82,6 +85,10 @@ static const char* const kAcceptLanguageList[] = { // Spanish speaking countries? "es", // Spanish "es-419", // Spanish (Latin America) + "es-AR", // Spanish (Argentina) + "es-ES", // Spanish (Spain) + "es-MX", // Spanish (Mexico) + "es-US", // Spanish (US) "et", // Estonian "eu", // Basque "fa", // Persian @@ -178,12 +185,14 @@ static const char* const kAcceptLanguageList[] = { "ur", // Urdu "uz", // Uzbek "vi", // Vietnamese + "wa", // Walloon "xh", // Xhosa "yi", // Yiddish "yo", // Yoruba "zh", // Chinese - "zh-CN", // Chinese (Simplified) - "zh-TW", // Chinese (Traditional) + "zh-CN", // Chinese (China) + "zh-HK", // Chinese (Hong Kong) + "zh-TW", // Chinese (Taiwan) "zu", // Zulu }; @@ -314,6 +323,8 @@ 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 +// and generic locale fallback based on ICU/CLDR. bool CheckAndResolveLocale(const std::string& locale, std::string* resolved_locale) { #if defined(OS_MACOSX) @@ -355,12 +366,13 @@ bool CheckAndResolveLocale(const std::string& locale, tmp_locale.append("-CN"); } } else if (base::LowerCaseEqualsASCII(lang, "en")) { - // Map Australian, Canadian, New Zealand and South African English - // to British English for now. + // Map Australian, Canadian, Indian, New Zealand and South African + // English to British English for now. // TODO(jungshik): en-CA may have to change sides once // we have OS locale separate from app locale (Chrome's UI language). if (base::LowerCaseEqualsASCII(region, "au") || base::LowerCaseEqualsASCII(region, "ca") || + base::LowerCaseEqualsASCII(region, "in") || base::LowerCaseEqualsASCII(region, "nz") || base::LowerCaseEqualsASCII(region, "za")) { tmp_locale.append("-GB"); diff --git a/chromium/ui/base/l10n/l10n_util_collator.h b/chromium/ui/base/l10n/l10n_util_collator.h index d2b95f26ae1..423f17a50da 100644 --- a/chromium/ui/base/l10n/l10n_util_collator.h +++ b/chromium/ui/base/l10n/l10n_util_collator.h @@ -22,10 +22,7 @@ namespace l10n_util { // Used by SortStringsUsingMethod. Invokes a method on the objects passed to // operator (), comparing the string results using a collator. template <class T, class Method> -class StringMethodComparatorWithCollator - : public std::binary_function<const base::string16&, - const base::string16&, - bool> { +class StringMethodComparatorWithCollator { public: StringMethodComparatorWithCollator(icu::Collator* collator, Method method) : collator_(collator), @@ -46,10 +43,7 @@ class StringMethodComparatorWithCollator // Used by SortStringsUsingMethod. Invokes a method on the objects passed to // operator (), comparing the string results using <. template <class T, class Method> -class StringMethodComparator - : public std::binary_function<const base::string16&, - const base::string16&, - bool> { +class StringMethodComparator { public: explicit StringMethodComparator(Method method) : method_(method) { } @@ -88,9 +82,7 @@ void SortStringsUsingMethod(const std::string& locale, // const base::string16& GetStringKey() const; // This uses the locale specified in the constructor. template <class Element> -class StringComparator : public std::binary_function<const Element&, - const Element&, - bool> { +class StringComparator { public: explicit StringComparator(icu::Collator* collator) : collator_(collator) { } diff --git a/chromium/ui/base/layout.cc b/chromium/ui/base/layout.cc index f4ebc0162c5..4299f097114 100644 --- a/chromium/ui/base/layout.cc +++ b/chromium/ui/base/layout.cc @@ -26,11 +26,6 @@ namespace { std::vector<ScaleFactor>* g_supported_scale_factors = NULL; -const float kScaleFactorScales[] = {1.0f, 1.0f, 1.25f, 1.33f, 1.4f, 1.5f, 1.8f, - 2.0f, 2.5f, 3.0f}; -static_assert(NUM_SCALE_FACTORS == arraysize(kScaleFactorScales), - "kScaleFactorScales has incorrect size"); - } // namespace void SetSupportedScaleFactors( @@ -50,7 +45,7 @@ void SetSupportedScaleFactors( for (std::vector<ScaleFactor>::const_iterator it = g_supported_scale_factors->begin(); it != g_supported_scale_factors->end(); ++it) { - scales.push_back(kScaleFactorScales[*it]); + scales.push_back(GetScaleForScaleFactor(*it)); } gfx::ImageSkia::SetSupportedScales(scales); } @@ -66,7 +61,7 @@ ScaleFactor GetSupportedScaleFactor(float scale) { float smallest_diff = std::numeric_limits<float>::max(); for (size_t i = 0; i < g_supported_scale_factors->size(); ++i) { ScaleFactor scale_factor = (*g_supported_scale_factors)[i]; - float diff = std::abs(kScaleFactorScales[scale_factor] - scale); + float diff = std::abs(GetScaleForScaleFactor(scale_factor) - scale); if (diff < smallest_diff) { closest_match = scale_factor; smallest_diff = diff; @@ -76,13 +71,9 @@ ScaleFactor GetSupportedScaleFactor(float scale) { return closest_match; } -float GetScaleForScaleFactor(ScaleFactor scale_factor) { - return kScaleFactorScales[scale_factor]; -} - bool IsSupportedScale(float scale) { for (auto scale_factor_idx : *g_supported_scale_factors) { - if (kScaleFactorScales[scale_factor_idx] == scale) + if (GetScaleForScaleFactor(scale_factor_idx) == scale) return true; } return false; @@ -115,9 +106,9 @@ ScopedSetSupportedScaleFactors::~ScopedSetSupportedScaleFactors() { #if !defined(OS_MACOSX) float GetScaleFactorForNativeView(gfx::NativeView view) { - gfx::Screen* screen = gfx::Screen::GetScreenFor(view); - gfx::Display display = screen->GetDisplayNearestWindow(view); - return display.device_scale_factor(); + return gfx::Screen::GetScreen() + ->GetDisplayNearestWindow(view) + .device_scale_factor(); } #endif // !defined(OS_MACOSX) diff --git a/chromium/ui/base/layout.h b/chromium/ui/base/layout.h index 8b528580f74..fd2627bbc50 100644 --- a/chromium/ui/base/layout.h +++ b/chromium/ui/base/layout.h @@ -9,31 +9,12 @@ #include "base/macros.h" #include "build/build_config.h" +#include "ui/base/resource/scale_factor.h" #include "ui/base/ui_base_export.h" #include "ui/gfx/native_widget_types.h" namespace ui { -// Supported UI scale factors for the platform. This is used as an index -// into the array |kScaleFactorScales| which maps the enum value to a float. -// SCALE_FACTOR_NONE is used for density independent resources such as -// string, html/js files or an image that can be used for any scale factors -// (such as wallpapers). -enum ScaleFactor : int { - SCALE_FACTOR_NONE = 0, - SCALE_FACTOR_100P, - SCALE_FACTOR_125P, - SCALE_FACTOR_133P, - SCALE_FACTOR_140P, - SCALE_FACTOR_150P, - SCALE_FACTOR_180P, - SCALE_FACTOR_200P, - SCALE_FACTOR_250P, - SCALE_FACTOR_300P, - - NUM_SCALE_FACTORS // This always appears last. -}; - // Changes the value of GetSupportedScaleFactors() to |scale_factors|. // Use ScopedSetSupportedScaleFactors for unit tests as not to affect the // state of other tests. @@ -52,9 +33,6 @@ UI_BASE_EXPORT ScaleFactor GetSupportedScaleFactor(float image_scale); // Returns the ScaleFactor used by |view|. UI_BASE_EXPORT float GetScaleFactorForNativeView(gfx::NativeView view); -// Returns the image scale for the scale factor passed in. -UI_BASE_EXPORT float GetScaleForScaleFactor(ScaleFactor scale_factor); - // Returns true if the scale passed in is the list of supported scales for // the platform. UI_BASE_EXPORT bool IsSupportedScale(float scale); diff --git a/chromium/ui/base/layout_mac.mm b/chromium/ui/base/layout_mac.mm index 62331e99f0d..27e12e1fa09 100644 --- a/chromium/ui/base/layout_mac.mm +++ b/chromium/ui/base/layout_mac.mm @@ -12,9 +12,7 @@ namespace { float GetScaleFactorScaleForNativeView(gfx::NativeView view) { if (NSWindow* window = [view window]) { - if ([window respondsToSelector:@selector(backingScaleFactor)]) - return [window backingScaleFactor]; - return [window userSpaceScaleFactor]; + return [window backingScaleFactor]; } NSArray* screens = [NSScreen screens]; @@ -22,9 +20,7 @@ float GetScaleFactorScaleForNativeView(gfx::NativeView view) { return 1.0f; NSScreen* screen = [screens objectAtIndex:0]; - if ([screen respondsToSelector:@selector(backingScaleFactor)]) - return [screen backingScaleFactor]; - return [screen userSpaceScaleFactor]; + return [screen backingScaleFactor]; } } // namespace diff --git a/chromium/ui/base/layout_unittest.cc b/chromium/ui/base/layout_unittest.cc index 4b0c5b1542c..7c975e6b155 100644 --- a/chromium/ui/base/layout_unittest.cc +++ b/chromium/ui/base/layout_unittest.cc @@ -14,18 +14,6 @@ namespace ui { -TEST(LayoutTest, GetScaleFactorScale) { - EXPECT_FLOAT_EQ(1.0f, GetScaleForScaleFactor(SCALE_FACTOR_100P)); - EXPECT_FLOAT_EQ(1.25f, GetScaleForScaleFactor(SCALE_FACTOR_125P)); - EXPECT_FLOAT_EQ(1.33f, GetScaleForScaleFactor(SCALE_FACTOR_133P)); - EXPECT_FLOAT_EQ(1.4f, GetScaleForScaleFactor(SCALE_FACTOR_140P)); - EXPECT_FLOAT_EQ(1.5f, GetScaleForScaleFactor(SCALE_FACTOR_150P)); - EXPECT_FLOAT_EQ(1.8f, GetScaleForScaleFactor(SCALE_FACTOR_180P)); - EXPECT_FLOAT_EQ(2.0f, GetScaleForScaleFactor(SCALE_FACTOR_200P)); - EXPECT_FLOAT_EQ(2.5f, GetScaleForScaleFactor(SCALE_FACTOR_250P)); - EXPECT_FLOAT_EQ(3.0f, GetScaleForScaleFactor(SCALE_FACTOR_300P)); -} - TEST(LayoutTest, GetScaleFactorFromScalePartlySupported) { std::vector<ScaleFactor> supported_factors; supported_factors.push_back(SCALE_FACTOR_100P); diff --git a/chromium/ui/base/resource/material_design/material_design_controller.cc b/chromium/ui/base/material_design/material_design_controller.cc index b19935e4962..411f553a8ba 100644 --- a/chromium/ui/base/resource/material_design/material_design_controller.cc +++ b/chromium/ui/base/material_design/material_design_controller.cc @@ -6,9 +6,22 @@ #include "base/command_line.h" #include "base/logging.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/ui_base_switches.h" +#if defined(OS_CHROMEOS) +#include "ui/base/touch/touch_device.h" +#include "ui/events/devices/device_data_manager.h" + +#if defined(USE_OZONE) +#include <fcntl.h> + +#include "base/files/file_enumerator.h" +#include "ui/events/ozone/evdev/event_device_info.h" +#endif // defined(USE_OZONE) + +#endif // defined(OS_CHROMEOS) + namespace ui { bool MaterialDesignController::is_mode_initialized_ = false; @@ -28,6 +41,39 @@ bool MaterialDesignController::IsModeMaterial() { GetMode() == Mode::MATERIAL_HYBRID; } +MaterialDesignController::Mode MaterialDesignController::DefaultMode() { +#if defined(OS_CHROMEOS) + if (DeviceDataManager::HasInstance() && + DeviceDataManager::GetInstance()->device_lists_complete()) { + return GetTouchScreensAvailability() == TouchScreensAvailability::ENABLED ? + Mode::MATERIAL_HYBRID : Mode::MATERIAL_NORMAL; + } + +#if defined(USE_OZONE) + base::FileEnumerator file_enum( + base::FilePath(FILE_PATH_LITERAL("/dev/input")), false, + base::FileEnumerator::FILES, FILE_PATH_LITERAL("event*[0-9]")); + for (base::FilePath path = file_enum.Next(); !path.empty(); + path = file_enum.Next()) { + EventDeviceInfo devinfo; + int fd = open(path.value().c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (fd >= 0) { + if (devinfo.Initialize(fd, path) && devinfo.HasTouchscreen()) { + close(fd); + return Mode::MATERIAL_HYBRID; + } + close(fd); + } + } +#endif // defined(USE_OZONE) + return Mode::MATERIAL_NORMAL; +#elif defined(OS_LINUX) + return Mode::MATERIAL_NORMAL; +#else + return Mode::NON_MATERIAL; +#endif // defined(OS_CHROMEOS) +} + void MaterialDesignController::InitializeMode() { #if !defined(ENABLE_TOPCHROME_MD) SetMode(Mode::NON_MATERIAL); @@ -43,10 +89,12 @@ void MaterialDesignController::InitializeMode() { } else if (switch_value == switches::kTopChromeMDNonMaterial) { SetMode(Mode::NON_MATERIAL); } else { - LOG(ERROR) << "Invalid value='" << switch_value - << "' for command line switch '" << switches::kTopChromeMD - << "'."; - SetMode(Mode::NON_MATERIAL); + if (!switch_value.empty()) { + LOG(ERROR) << "Invalid value='" << switch_value + << "' for command line switch '" << switches::kTopChromeMD + << "'."; + } + SetMode(DefaultMode()); } #endif // !defined(ENABLE_TOPCHROME_MD) } diff --git a/chromium/ui/base/resource/material_design/material_design_controller.h b/chromium/ui/base/material_design/material_design_controller.h index af92f4d62bc..9ef61c0c4e5 100644 --- a/chromium/ui/base/resource/material_design/material_design_controller.h +++ b/chromium/ui/base/material_design/material_design_controller.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_BASE_RESOURCE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_H_ -#define UI_BASE_RESOURCE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_H_ +#ifndef UI_BASE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_H_ +#define UI_BASE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_H_ #include "base/macros.h" #include "ui/base/ui_base_export.h" @@ -34,6 +34,9 @@ class UI_BASE_EXPORT MaterialDesignController { // Returns true if the current mode is a material design variant. static bool IsModeMaterial(); + // Returns the per-platform default material design variant. + static Mode DefaultMode(); + private: friend class test::MaterialDesignControllerTestAPI; @@ -64,4 +67,4 @@ class UI_BASE_EXPORT MaterialDesignController { } // namespace ui -#endif // UI_BASE_RESOURCE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_H_ +#endif // UI_BASE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_H_ diff --git a/chromium/ui/base/resource/material_design/material_design_controller_unittest.cc b/chromium/ui/base/material_design/material_design_controller_unittest.cc index cfc76b59493..543cf3d2c8f 100644 --- a/chromium/ui/base/resource/material_design/material_design_controller_unittest.cc +++ b/chromium/ui/base/material_design/material_design_controller_unittest.cc @@ -7,7 +7,7 @@ #include "base/command_line.h" #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/test/material_design_controller_test_api.h" #include "ui/base/ui_base_switches.h" @@ -77,33 +77,33 @@ TEST_F( MaterialDesignController::GetMode()); } -// Verify command line value "" maps to Mode::NON_MATERIAL when the compile time +// Verify command line value "" maps to the default mode when the compile time // flag is defined. TEST_F( MaterialDesignControllerTest, DisabledCommandLineValueMapsToNonMaterialModeWhenCompileTimeFlagEnabled) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kTopChromeMD, ""); - EXPECT_EQ(MaterialDesignController::Mode::NON_MATERIAL, + EXPECT_EQ(MaterialDesignController::DefaultMode(), MaterialDesignController::GetMode()); } -// Verify no command line value maps to Mode::NON_MATERIAL when the compile time +// Verify the current mode is reported as the default mode when no command line // flag is defined. TEST_F(MaterialDesignControllerTest, NoCommandLineValueMapsToNonMaterialModeWhenCompileTimeFlagEnabled) { ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kTopChromeMD)); - EXPECT_EQ(MaterialDesignController::Mode::NON_MATERIAL, + EXPECT_EQ(MaterialDesignController::DefaultMode(), MaterialDesignController::GetMode()); } -// Verify an invalid command line value uses the default NON_MATERIAL mode. +// Verify an invalid command line value uses the default mode. TEST_F(MaterialDesignControllerTest, InvalidCommandLineValue) { const std::string kInvalidValue = "1nvalid-valu3"; base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kTopChromeMD, kInvalidValue); - EXPECT_EQ(MaterialDesignController::Mode::NON_MATERIAL, + EXPECT_EQ(MaterialDesignController::DefaultMode(), MaterialDesignController::GetMode()); } diff --git a/chromium/ui/base/models/dialog_model.h b/chromium/ui/base/models/dialog_model.h index 94f85e98e7a..5607a1675b0 100644 --- a/chromium/ui/base/models/dialog_model.h +++ b/chromium/ui/base/models/dialog_model.h @@ -18,9 +18,6 @@ class UI_BASE_EXPORT DialogModel { public: virtual ~DialogModel(); - // Returns the title of the dialog. - virtual base::string16 GetDialogTitle() const = 0; - // Returns a mask specifying which of the available DialogButtons are visible // for the dialog. Note: Dialogs with just an OK button are frowned upon. virtual int GetDialogButtons() const = 0; diff --git a/chromium/ui/base/models/table_model.cc b/chromium/ui/base/models/table_model.cc index b8b18ad0a77..d98cdb2a571 100644 --- a/chromium/ui/base/models/table_model.cc +++ b/chromium/ui/base/models/table_model.cc @@ -35,6 +35,8 @@ TableColumn::TableColumn(int id, Alignment alignment, int width, float percent) initial_sort_is_ascending(true) { } +TableColumn::TableColumn(const TableColumn& other) = default; + // TableModel ----------------------------------------------------------------- // Used for sorting. diff --git a/chromium/ui/base/models/table_model.h b/chromium/ui/base/models/table_model.h index 16e68a94f3a..c7172f4f5d1 100644 --- a/chromium/ui/base/models/table_model.h +++ b/chromium/ui/base/models/table_model.h @@ -98,6 +98,7 @@ struct UI_BASE_EXPORT TableColumn { TableColumn(); TableColumn(int id, Alignment alignment, int width, float percent); + TableColumn(const TableColumn& other); // A unique identifier for the column. int id; diff --git a/chromium/ui/base/models/tree_node_model.h b/chromium/ui/base/models/tree_node_model.h index 9606dc8119c..279667b2a40 100644 --- a/chromium/ui/base/models/tree_node_model.h +++ b/chromium/ui/base/models/tree_node_model.h @@ -10,12 +10,11 @@ #include <algorithm> #include <vector> -#include "base/compiler_specific.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" #include "base/observer_list.h" +#include "base/stl_util.h" #include "base/strings/string16.h" #include "ui/base/models/tree_model.h" @@ -70,7 +69,9 @@ class TreeNode : public TreeModelNode { explicit TreeNode(const base::string16& title) : title_(title), parent_(NULL) {} - ~TreeNode() override {} + ~TreeNode() override { + STLDeleteElements(&children_); + } // Adds |node| as a child of this node, at |index|. virtual void Add(NodeType* node, int index) { @@ -92,7 +93,7 @@ class TreeNode : public TreeModelNode { std::find(children_.begin(), children_.end(), node); DCHECK(i != children_.end()); node->parent_ = NULL; - children_.weak_erase(i); + children_.erase(i); return node; } @@ -100,7 +101,7 @@ class TreeNode : public TreeModelNode { void RemoveAll() { for (size_t i = 0; i < children_.size(); ++i) children_[i]->parent_ = NULL; - children_.weak_clear(); + children_.clear(); } // Removes all existing children without deleting the nodes and adds all nodes @@ -169,7 +170,7 @@ class TreeNode : public TreeModelNode { } protected: - std::vector<NodeType*>& children() { return children_.get(); } + std::vector<NodeType*>& children() { return children_; } private: // Title displayed in the tree. @@ -179,7 +180,7 @@ class TreeNode : public TreeModelNode { NodeType* parent_; // This node's children. - ScopedVector<NodeType> children_; + std::vector<NodeType*> children_; DISALLOW_COPY_AND_ASSIGN(TreeNode); }; diff --git a/chromium/ui/base/nine_image_painter_factory.cc b/chromium/ui/base/nine_image_painter_factory.cc index 691e4ae8817..75281a2b06f 100644 --- a/chromium/ui/base/nine_image_painter_factory.cc +++ b/chromium/ui/base/nine_image_painter_factory.cc @@ -14,6 +14,7 @@ namespace ui { namespace { std::vector<gfx::ImageSkia> ImageIdsToImages(const int image_ids[]) { + DCHECK(image_ids); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); std::vector<gfx::ImageSkia> images(9); for (size_t i = 0; i < 9; ++i) { diff --git a/chromium/ui/base/resource/data_pack.h b/chromium/ui/base/resource/data_pack.h index b2d90684c0f..1292203a6bb 100644 --- a/chromium/ui/base/resource/data_pack.h +++ b/chromium/ui/base/resource/data_pack.h @@ -20,8 +20,8 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/strings/string_piece.h" +#include "ui/base/resource/data_pack_export.h" #include "ui/base/resource/resource_handle.h" -#include "ui/base/ui_base_export.h" namespace base { class FilePath; @@ -31,7 +31,7 @@ class RefCountedStaticMemory; namespace ui { enum ScaleFactor : int; -class UI_BASE_EXPORT DataPack : public ResourceHandle { +class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { public: explicit DataPack(ui::ScaleFactor scale_factor); ~DataPack() override; diff --git a/chromium/ui/base/resource/data_pack_export.h b/chromium/ui/base/resource/data_pack_export.h new file mode 100644 index 00000000000..88e908d5cbe --- /dev/null +++ b/chromium/ui/base/resource/data_pack_export.h @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_RESOURCE_DATA_PACK_EXPORT_H_ +#define UI_BASE_RESOURCE_DATA_PACK_EXPORT_H_ + +// Defines UI_DATA_PACK_EXPORT so that functionality implemented by the data +// pack loading module can be exported to consumers. + +#if defined(COMPONENT_BUILD) + +#if defined(WIN32) + +#if defined(UI_DATA_PACK_IMPLEMENTATION) +#define UI_DATA_PACK_EXPORT __declspec(dllexport) +#else +#define UI_DATA_PACK_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(UI_DATA_PACK_IMPLEMENTATION) +#define UI_DATA_PACK_EXPORT __attribute__((visibility("default"))) +#else +#define UI_DATA_PACK_EXPORT +#endif + +#endif + +#else // !defined(COMPONENT_BUILD) + +#define UI_DATA_PACK_EXPORT + +#endif + +#endif // UI_BASE_RESOURCE_DATA_PACK_EXPORT_H_ diff --git a/chromium/ui/base/resource/resource_bundle.cc b/chromium/ui/base/resource/resource_bundle.cc index a2aa5824a19..93230b42da4 100644 --- a/chromium/ui/base/resource/resource_bundle.cc +++ b/chromium/ui/base/resource/resource_bundle.cc @@ -28,10 +28,11 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/layout.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/data_pack.h" -#include "ui/base/resource/material_design/material_design_controller.h" #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" +#include "ui/base/ui_features.h" #include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/safe_integer_conversions.h" @@ -62,11 +63,6 @@ namespace ui { namespace { -// Font sizes relative to base font. -const int kSmallFontSizeDelta = -1; -const int kMediumFontSizeDelta = 3; -const int kLargeFontSizeDelta = 8; - // PNG-related constants. const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 }; const size_t kPngChunkMetadataSize = 12; // length, type, crc32 @@ -394,12 +390,14 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { DCHECK(!data_packs_.empty()) << "Missing call to SetResourcesDataDLL?"; -#if defined(OS_CHROMEOS) || defined(OS_WIN) - ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor(); +#if defined(OS_CHROMEOS) + ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor(); +#elif defined(OS_WIN) + ui::ScaleFactor scale_factor_to_load = + gfx::GetDPIScale() > 1.25 ? GetMaxScaleFactor() : ui::SCALE_FACTOR_100P; #else - ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P; + ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P; #endif - // TODO(oshima): Consider reading the image size from png IHDR chunk and // skip decoding here and remove #ifdef below. // ResourceBundle::GetSharedInstance() is destroyed after the @@ -428,10 +426,6 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { return images_[resource_id]; } -gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { - return GetNativeImageNamed(resource_id, RTL_DISABLED); -} - base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes( int resource_id) const { return LoadDataResourceBytesForScale(resource_id, ui::SCALE_FACTOR_NONE); @@ -539,29 +533,72 @@ base::string16 ResourceBundle::GetLocalizedString(int message_id) { return msg; } -const gfx::FontList& ResourceBundle::GetFontList(FontStyle style) { - { - base::AutoLock lock_scope(*images_and_fonts_lock_); - LoadFontsIfNecessary(); - } - switch (style) { - case BoldFont: - return *bold_font_list_; +const gfx::FontList& ResourceBundle::GetFontListWithDelta( + int size_delta, + gfx::Font::FontStyle style) { + base::AutoLock lock_scope(*images_and_fonts_lock_); + + typedef std::pair<int, gfx::Font::FontStyle> Key; + const Key styled_key(size_delta, style); + + auto found = font_cache_.find(styled_key); + if (found != font_cache_.end()) + return found->second; + + const Key base_key(0, gfx::Font::NORMAL); + gfx::FontList& base = font_cache_[base_key]; + if (styled_key == base_key) + return base; + + // Fonts of a given style are derived from the unstyled font of the same size. + // Cache the unstyled font by first inserting a default-constructed font list. + // Then, derive it for the initial insertion, or use the iterator that points + // to the existing entry that the insertion collided with. + const Key sized_key(size_delta, gfx::Font::NORMAL); + auto sized = font_cache_.insert(std::make_pair(sized_key, gfx::FontList())); + if (sized.second) + sized.first->second = base.DeriveWithSizeDelta(size_delta); + if (styled_key == sized_key) + return sized.first->second; + + auto styled = font_cache_.insert(std::make_pair(styled_key, gfx::FontList())); + DCHECK(styled.second); // Otherwise font_cache_.find(..) would have found it. + styled.first->second = sized.first->second.DeriveWithStyle( + sized.first->second.GetFontStyle() | style); + return styled.first->second; +} + +const gfx::Font& ResourceBundle::GetFontWithDelta(int size_delta, + gfx::Font::FontStyle style) { + return GetFontListWithDelta(size_delta, style).GetPrimaryFont(); +} + +const gfx::FontList& ResourceBundle::GetFontList(FontStyle legacy_style) { + gfx::Font::FontStyle font_style = gfx::Font::NORMAL; + if (legacy_style == BoldFont || legacy_style == SmallBoldFont || + legacy_style == MediumBoldFont || legacy_style == LargeBoldFont) + font_style = gfx::Font::BOLD; + + int size_delta = 0; + switch (legacy_style) { case SmallFont: - return *small_font_list_; - case MediumFont: - return *medium_font_list_; case SmallBoldFont: - return *small_bold_font_list_; + size_delta = kSmallFontDelta; + break; + case MediumFont: case MediumBoldFont: - return *medium_bold_font_list_; + size_delta = kMediumFontDelta; + break; case LargeFont: - return *large_font_list_; case LargeBoldFont: - return *large_bold_font_list_; - default: - return *base_font_list_; + size_delta = kLargeFontDelta; + break; + case BaseFont: + case BoldFont: + break; } + + return GetFontListWithDelta(size_delta, font_style); } const gfx::Font& ResourceBundle::GetFont(FontStyle style) { @@ -571,12 +608,11 @@ const gfx::Font& ResourceBundle::GetFont(FontStyle style) { void ResourceBundle::ReloadFonts() { base::AutoLock lock_scope(*images_and_fonts_lock_); InitDefaultFontList(); - base_font_list_.reset(); - LoadFontsIfNecessary(); + font_cache_.clear(); } ScaleFactor ResourceBundle::GetMaxScaleFactor() const { -#if defined(OS_CHROMEOS) || defined(OS_WIN) +#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_LINUX) return max_scale_factor_; #else return GetSupportedScaleFactors().back(); @@ -608,7 +644,7 @@ void ResourceBundle::InitSharedInstance(Delegate* delegate) { DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; g_shared_instance_ = new ResourceBundle(delegate); static std::vector<ScaleFactor> supported_scale_factors; -#if !defined(OS_IOS) && !defined(OS_WIN) +#if !defined(OS_IOS) // On platforms other than iOS, 100P is always a supported scale factor. // For Windows we have a separate case in this function. supported_scale_factors.push_back(SCALE_FACTOR_100P); @@ -625,7 +661,7 @@ void ResourceBundle::InitSharedInstance(Delegate* delegate) { if (closest != SCALE_FACTOR_100P) supported_scale_factors.push_back(closest); #elif defined(OS_IOS) - gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); + gfx::Display display = gfx::Screen::GetScreen()->GetPrimaryDisplay(); if (display.device_scale_factor() > 2.0) { DCHECK_EQ(3.0, display.device_scale_factor()); supported_scale_factors.push_back(SCALE_FACTOR_300P); @@ -638,22 +674,8 @@ void ResourceBundle::InitSharedInstance(Delegate* delegate) { #elif defined(OS_MACOSX) if (base::mac::IsOSLionOrLater()) supported_scale_factors.push_back(SCALE_FACTOR_200P); -#elif defined(OS_CHROMEOS) - // TODO(oshima): Include 200P only if the device support 200P +#elif defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN) supported_scale_factors.push_back(SCALE_FACTOR_200P); -#elif defined(OS_LINUX) && defined(ENABLE_HIDPI) - supported_scale_factors.push_back(SCALE_FACTOR_200P); -#elif defined(OS_WIN) - bool default_to_100P = true; - // On Windows if the dpi scale is greater than 1.25 on high dpi machines - // downscaling from 200 percent looks better than scaling up from 100 - // percent. - if (gfx::GetDPIScale() > 1.25) { - supported_scale_factors.push_back(SCALE_FACTOR_200P); - default_to_100P = false; - } - if (default_to_100P) - supported_scale_factors.push_back(SCALE_FACTOR_100P); #endif ui::SetSupportedScaleFactors(supported_scale_factors); } @@ -749,76 +771,6 @@ void ResourceBundle::InitDefaultFontList() { #endif } -void ResourceBundle::LoadFontsIfNecessary() { - images_and_fonts_lock_->AssertAcquired(); - if (!base_font_list_.get()) { - if (delegate_) { - base_font_list_ = GetFontListFromDelegate(BaseFont); - bold_font_list_ = GetFontListFromDelegate(BoldFont); - small_font_list_ = GetFontListFromDelegate(SmallFont); - small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont); - medium_font_list_ = GetFontListFromDelegate(MediumFont); - medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont); - large_font_list_ = GetFontListFromDelegate(LargeFont); - large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont); - } - - if (!base_font_list_.get()) - base_font_list_.reset(new gfx::FontList()); - - if (!bold_font_list_.get()) { - bold_font_list_.reset(new gfx::FontList()); - *bold_font_list_ = base_font_list_->DeriveWithStyle( - base_font_list_->GetFontStyle() | gfx::Font::BOLD); - } - - if (!small_font_list_.get()) { - small_font_list_.reset(new gfx::FontList()); - *small_font_list_ = - base_font_list_->DeriveWithSizeDelta(kSmallFontSizeDelta); - } - - if (!small_bold_font_list_.get()) { - small_bold_font_list_.reset(new gfx::FontList()); - *small_bold_font_list_ = small_font_list_->DeriveWithStyle( - small_font_list_->GetFontStyle() | gfx::Font::BOLD); - } - - if (!medium_font_list_.get()) { - medium_font_list_.reset(new gfx::FontList()); - *medium_font_list_ = - base_font_list_->DeriveWithSizeDelta(kMediumFontSizeDelta); - } - - if (!medium_bold_font_list_.get()) { - medium_bold_font_list_.reset(new gfx::FontList()); - *medium_bold_font_list_ = medium_font_list_->DeriveWithStyle( - medium_font_list_->GetFontStyle() | gfx::Font::BOLD); - } - - if (!large_font_list_.get()) { - large_font_list_.reset(new gfx::FontList()); - *large_font_list_ = - base_font_list_->DeriveWithSizeDelta(kLargeFontSizeDelta); - } - - if (!large_bold_font_list_.get()) { - large_bold_font_list_.reset(new gfx::FontList()); - *large_bold_font_list_ = large_font_list_->DeriveWithStyle( - large_font_list_->GetFontStyle() | gfx::Font::BOLD); - } - } -} - -scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate( - FontStyle style) { - DCHECK(delegate_); - scoped_ptr<gfx::Font> font = delegate_->GetFont(style); - if (font.get()) - return make_scoped_ptr(new gfx::FontList(*font)); - return nullptr; -} - bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, int resource_id, SkBitmap* bitmap, diff --git a/chromium/ui/base/resource/resource_bundle.h b/chromium/ui/base/resource/resource_bundle.h index 3e169a469bd..61f2861b1e9 100644 --- a/chromium/ui/base/resource/resource_bundle.h +++ b/chromium/ui/base/resource/resource_bundle.h @@ -43,12 +43,16 @@ class ResourceHandle; // such as theme graphics. Every resource is loaded only once. class UI_BASE_EXPORT ResourceBundle { public: - // An enumeration of the various font styles used throughout Chrome. - // The following holds true for the font sizes: - // Small <= SmallBold <= Base <= Bold <= Medium <= MediumBold <= Large. + // Legacy font size deltas. Consider these to be magic numbers. New code + // should declare their own size delta constant using an identifier that + // imparts some semantic meaning. + static const int kSmallFontDelta = -1; + static const int kMediumFontDelta = 3; + static const int kLargeFontDelta = 8; + + // Legacy font style mappings. TODO(tapted): Phase these out in favour of + // client code providing their own constant with the desired font size delta. enum FontStyle { - // NOTE: depending upon the locale, using one of the *BoldFont below - // may *not* actually result in a bold font. SmallFont, SmallBoldFont, BaseFont, @@ -59,13 +63,6 @@ class UI_BASE_EXPORT ResourceBundle { LargeBoldFont, }; - enum ImageRTL { - // Images are flipped in RTL locales. - RTL_ENABLED, - // Images are never flipped. - RTL_DISABLED, - }; - enum LoadResources { LOAD_COMMON_RESOURCES, DO_NOT_LOAD_COMMON_RESOURCES @@ -97,7 +94,7 @@ class UI_BASE_EXPORT ResourceBundle { // Return an image resource or an empty value to attempt retrieval of the // default resource. - virtual gfx::Image GetNativeImageNamed(int resource_id, ImageRTL rtl) = 0; + virtual gfx::Image GetNativeImageNamed(int resource_id) = 0; // Return a static memory resource or NULL to attempt retrieval of the // default resource. @@ -115,9 +112,6 @@ class UI_BASE_EXPORT ResourceBundle { // false to attempt retrieval of the default string. virtual bool GetLocalizedString(int message_id, base::string16* value) = 0; - // Returns a font or NULL to attempt retrieval of the default resource. - virtual scoped_ptr<gfx::Font> GetFont(FontStyle style) = 0; - protected: virtual ~Delegate() {} }; @@ -219,11 +213,6 @@ class UI_BASE_EXPORT ResourceBundle { // Note that if the same resource has already been loaded in GetImageNamed(), // gfx::Image will perform a conversion, rather than using the native image // loading code of ResourceBundle. - // - // If |rtl| is RTL_ENABLED then the image is flipped in RTL locales. - gfx::Image& GetNativeImageNamed(int resource_id, ImageRTL rtl); - - // Same as GetNativeImageNamed() except that RTL is not enabled. gfx::Image& GetNativeImageNamed(int resource_id); // Loads the raw bytes of a scale independent data resource. @@ -253,10 +242,19 @@ class UI_BASE_EXPORT ResourceBundle { // string if the message_id is not found. base::string16 GetLocalizedString(int message_id); - // Returns the font list for the specified style. - const gfx::FontList& GetFontList(FontStyle style); + // Returns a font list derived from the platform-specific "Base" font list. + // The result is always cached and exists for the lifetime of the process. + const gfx::FontList& GetFontListWithDelta( + int size_delta, + gfx::Font::FontStyle style = gfx::Font::NORMAL); - // Returns the font for the specified style. + // Returns the primary font from the FontList given by GetFontListWithDelta(). + const gfx::Font& GetFontWithDelta( + int size_delta, + gfx::Font::FontStyle style = gfx::Font::NORMAL); + + // Deprecated. Returns fonts using hard-coded size deltas implied by |style|. + const gfx::FontList& GetFontList(FontStyle style); const gfx::Font& GetFont(FontStyle style); // Resets and reloads the cached fonts. This is useful when the fonts of the @@ -287,6 +285,12 @@ class UI_BASE_EXPORT ResourceBundle { // Returns SCALE_FACTOR_100P if no resource is loaded. ScaleFactor GetMaxScaleFactor() const; +#if defined(OS_MACOSX) + // Loads Material Design data packs and makes them the first items in + // |data_packs_|. + void LoadMaterialDesignResources(); +#endif + protected: // Returns true if |scale_factor| is supported by this platform. static bool IsScaleFactorSupported(ScaleFactor scale_factor); @@ -299,10 +303,13 @@ class UI_BASE_EXPORT ResourceBundle { CountMaterialDesignDataPacksInResourceBundle); FRIEND_TEST_ALL_PREFIXES(ResourceBundleMacImageTest, CheckImageFromMaterialDesign); + FRIEND_TEST_ALL_PREFIXES(ChromeBrowserMainMacBrowserTest, + MDResourceAccess); friend class ResourceBundleMacImageTest; friend class ResourceBundleImageTest; friend class ResourceBundleTest; + friend class ChromeBrowserMainMacBrowserTest; class ResourceBundleImageSource; friend class ResourceBundleImageSource; @@ -356,14 +363,6 @@ class UI_BASE_EXPORT ResourceBundle { // Initializes the font description of default gfx::FontList. void InitDefaultFontList(); - // Initializes all the gfx::FontList members if they haven't yet been - // initialized. - void LoadFontsIfNecessary(); - - // Returns a FontList or NULL by calling Delegate::GetFont and converting - // scoped_ptr<gfx::Font> to scoped_ptr<gfx::FontList>. - scoped_ptr<gfx::FontList> GetFontListFromDelegate(FontStyle style); - // Fills the |bitmap| given the data file to look in and the |resource_id|. // Returns false if the resource does not exist. // @@ -430,17 +429,11 @@ class UI_BASE_EXPORT ResourceBundle { gfx::Image empty_image_; - // The various font lists used. Cached to avoid repeated GDI - // creation/destruction. - scoped_ptr<gfx::FontList> base_font_list_; - scoped_ptr<gfx::FontList> bold_font_list_; - scoped_ptr<gfx::FontList> small_font_list_; - scoped_ptr<gfx::FontList> small_bold_font_list_; - scoped_ptr<gfx::FontList> medium_font_list_; - scoped_ptr<gfx::FontList> medium_bold_font_list_; - scoped_ptr<gfx::FontList> large_font_list_; - scoped_ptr<gfx::FontList> large_bold_font_list_; - scoped_ptr<gfx::FontList> web_font_list_; + // The various font lists used, as a map from a signed size delta from the + // platform base font size, plus style, to the FontList. Cached to avoid + // repeated GDI creation/destruction and font derivation. + // Must be accessed only while holding |images_and_fonts_lock_|. + std::map<std::pair<int, gfx::Font::FontStyle>, gfx::FontList> font_cache_; base::FilePath overridden_pak_path_; diff --git a/chromium/ui/base/resource/resource_bundle_android.cc b/chromium/ui/base/resource/resource_bundle_android.cc index ae731d680cb..e19a8b8301d 100644 --- a/chromium/ui/base/resource/resource_bundle_android.cc +++ b/chromium/ui/base/resource/resource_bundle_android.cc @@ -120,9 +120,7 @@ std::string ResourceBundle::LoadLocaleResources( return app_locale; } -gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { - // Flipped image is not used on Android. - DCHECK_EQ(rtl, RTL_DISABLED); +gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { return GetImageNamed(resource_id); } diff --git a/chromium/ui/base/resource/resource_bundle_auralinux.cc b/chromium/ui/base/resource/resource_bundle_auralinux.cc index 7365b1df3d1..8989a9e49c4 100644 --- a/chromium/ui/base/resource/resource_bundle_auralinux.cc +++ b/chromium/ui/base/resource/resource_bundle_auralinux.cc @@ -20,9 +20,7 @@ void ResourceBundle::LoadCommonResources() { LoadChromeResources(); } -gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { - // Flipped image is not used on ChromeOS. - DCHECK_EQ(rtl, RTL_DISABLED); +gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { return GetImageNamed(resource_id); } diff --git a/chromium/ui/base/resource/resource_bundle_ios.mm b/chromium/ui/base/resource/resource_bundle_ios.mm index 88cf829c6dd..17f1c79ac07 100644 --- a/chromium/ui/base/resource/resource_bundle_ios.mm +++ b/chromium/ui/base/resource/resource_bundle_ios.mm @@ -89,10 +89,7 @@ base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, return locale_file_path; } -gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { - // Flipped images are not used on iOS. - DCHECK_EQ(rtl, RTL_DISABLED); - +gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { // Check to see if the image is already in the cache. { base::AutoLock lock(*images_and_fonts_lock_); @@ -104,7 +101,7 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { gfx::Image image; if (delegate_) - image = delegate_->GetNativeImageNamed(resource_id, rtl); + image = delegate_->GetNativeImageNamed(resource_id); if (image.IsEmpty()) { // Load the raw data from the resource pack at the current supported scale diff --git a/chromium/ui/base/resource/resource_bundle_mac.mm b/chromium/ui/base/resource/resource_bundle_mac.mm index 74d82615606..0b8d526c9a6 100644 --- a/chromium/ui/base/resource/resource_bundle_mac.mm +++ b/chromium/ui/base/resource/resource_bundle_mac.mm @@ -14,7 +14,7 @@ #include "base/memory/ref_counted_memory.h" #include "base/strings/sys_string_conversions.h" #include "base/synchronization/lock.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_handle.h" #include "ui/gfx/image/image.h" @@ -49,20 +49,6 @@ base::FilePath GetResourcesPakFilePath(NSString* name, NSString* mac_locale) { } // namespace void ResourceBundle::LoadCommonResources() { - // The material design data packs contain some of the same asset IDs as in - // the non-material design data packs. Add these to the ResourceBundle - // first so that they are searched first when a request for an asset is - // made. - if (MaterialDesignController::IsModeMaterial()) { - AddMaterialDesignDataPackFromPath( - GetResourcesPakFilePath(@"chrome_material_100_percent", nil), - SCALE_FACTOR_100P); - - AddOptionalMaterialDesignDataPackFromPath( - GetResourcesPakFilePath(@"chrome_material_200_percent", nil), - SCALE_FACTOR_200P); - } - AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_100_percent", nil), SCALE_FACTOR_100P); @@ -74,6 +60,40 @@ void ResourceBundle::LoadCommonResources() { } } +void ResourceBundle::LoadMaterialDesignResources() { + if (!MaterialDesignController::IsModeMaterial()) { + return; + } + + // The Material Design data packs contain some of the same asset IDs as in + // the non-Material Design data packs. Set aside the current packs and add the + // Material Design packs so that they are searched first when a request for an + // asset is made. The Material Design packs cannot be loaded in + // LoadCommonResources() because the MaterialDesignController is not always + // initialized at the time it is called. + // TODO(shrike) - remove this method and restore loading of Material Design + // packs to LoadCommonResources() when the MaterialDesignController goes away. + std::vector<scoped_ptr<ResourceHandle>> tmp_packs; + for (auto it = data_packs_.begin(); it != data_packs_.end(); ++it) { + scoped_ptr<ResourceHandle> next_pack(*it); + tmp_packs.push_back(std::move(next_pack)); + } + data_packs_.weak_clear(); + + AddMaterialDesignDataPackFromPath( + GetResourcesPakFilePath(@"chrome_material_100_percent", nil), + SCALE_FACTOR_100P); + + AddOptionalMaterialDesignDataPackFromPath( + GetResourcesPakFilePath(@"chrome_material_200_percent", nil), + SCALE_FACTOR_200P); + + // Add back the non-Material Design packs so that they serve as a fallback. + for (auto it = tmp_packs.begin(); it != tmp_packs.end(); ++it) { + data_packs_.push_back(std::move(*it)); + } +} + base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, bool test_file_exists) { NSString* mac_locale = base::SysUTF8ToNSString(app_locale); @@ -104,10 +124,7 @@ base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, return locale_file_path; } -gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { - // Flipped images are not used on Mac. - DCHECK_EQ(rtl, RTL_DISABLED); - +gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { // Check to see if the image is already in the cache. { base::AutoLock lock(*images_and_fonts_lock_); @@ -124,29 +141,45 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { gfx::Image image; if (delegate_) - image = delegate_->GetNativeImageNamed(resource_id, rtl); - - bool found_in_a_material_design_pack = false; + image = delegate_->GetNativeImageNamed(resource_id); if (image.IsEmpty()) { base::scoped_nsobject<NSImage> ns_image; + // Material Design packs are meant to override the standard packs, so + // search for the image in those packs first. for (size_t i = 0; i < data_packs_.size(); ++i) { + if (!data_packs_[i]->HasOnlyMaterialDesignAssets()) + continue; scoped_refptr<base::RefCountedStaticMemory> data( data_packs_[i]->GetStaticMemory(resource_id)); if (!data.get()) continue; - // This loop adds the image resource from each available pack, if it's - // present. When Material Design packs are available, however, their - // images are meant to override the same image in the standard packs. The - // Material Design packs exist at the start of the data_packs_ vector, - // so make a note that the image was pulled from a Material Design pack, - // and ignore the same image in the standard packs. - if (found_in_a_material_design_pack) { - break; + base::scoped_nsobject<NSData> ns_data( + [[NSData alloc] initWithBytes:data->front() length:data->size()]); + if (!ns_image.get()) { + ns_image.reset([[NSImage alloc] initWithData:ns_data]); + } else { + NSImageRep* image_rep = [NSBitmapImageRep imageRepWithData:ns_data]; + if (image_rep) + [ns_image addRepresentation:image_rep]; } - found_in_a_material_design_pack = - data_packs_[i]->HasOnlyMaterialDesignAssets(); + } + + if (ns_image.get()) { + image = gfx::Image(ns_image.release()); + } + } + + if (image.IsEmpty()) { + base::scoped_nsobject<NSImage> ns_image; + for (size_t i = 0; i < data_packs_.size(); ++i) { + if (data_packs_[i]->HasOnlyMaterialDesignAssets()) + continue; + scoped_refptr<base::RefCountedStaticMemory> data( + data_packs_[i]->GetStaticMemory(resource_id)); + if (!data.get()) + continue; base::scoped_nsobject<NSData> ns_data( [[NSData alloc] initWithBytes:data->front() length:data->size()]); diff --git a/chromium/ui/base/resource/resource_bundle_unittest.cc b/chromium/ui/base/resource/resource_bundle_unittest.cc index eac9ef37306..0ada42e28be 100644 --- a/chromium/ui/base/resource/resource_bundle_unittest.cc +++ b/chromium/ui/base/resource/resource_bundle_unittest.cc @@ -72,9 +72,7 @@ class MockResourceBundleDelegate : public ui::ResourceBundle::Delegate { MOCK_METHOD2(GetPathForLocalePack, base::FilePath( const base::FilePath& pack_path, const std::string& locale)); MOCK_METHOD1(GetImageNamed, gfx::Image(int resource_id)); - MOCK_METHOD2(GetNativeImageNamed, - gfx::Image(int resource_id, - ui::ResourceBundle::ImageRTL rtl)); + MOCK_METHOD1(GetNativeImageNamed, gfx::Image(int resource_id)); MOCK_METHOD2(LoadDataResourceBytes, base::RefCountedStaticMemory*(int resource_id, ui::ScaleFactor scale_factor)); @@ -93,11 +91,6 @@ class MockResourceBundleDelegate : public ui::ResourceBundle::Delegate { *value = GetLocalizedStringMock(message_id); return true; } - MOCK_METHOD1(GetFontMock, - gfx::Font*(ui::ResourceBundle::FontStyle style)); - scoped_ptr<gfx::Font> GetFont(ui::ResourceBundle::FontStyle style) override { - return make_scoped_ptr(GetFontMock(style)); - } }; // Returns |bitmap_data| with |custom_chunk| inserted after the IHDR chunk. @@ -250,7 +243,7 @@ TEST_F(ResourceBundleTest, DelegateGetNativeImageNamed) { .Times(Between(0, 1)) .WillOnce(Return(empty_image)); EXPECT_CALL(delegate, - GetNativeImageNamed(resource_id, ui::ResourceBundle::RTL_DISABLED)) + GetNativeImageNamed(resource_id)) .Times(Between(0, 1)) .WillOnce(Return(empty_image)); @@ -344,59 +337,6 @@ TEST_F(ResourceBundleTest, DelegateGetLocalizedStringWithOverride) { EXPECT_EQ(delegate_data, result); } -#if (defined(USE_OZONE) && !defined(USE_PANGO)) || defined(OS_ANDROID) -#define MAYBE_DelegateGetFontList DISABLED_DelegateGetFontList -#else -#define MAYBE_DelegateGetFontList DelegateGetFontList -#endif - -TEST_F(ResourceBundleTest, MAYBE_DelegateGetFontList) { - MockResourceBundleDelegate delegate; - ResourceBundle* resource_bundle = CreateResourceBundle(&delegate); - - // Should be called once for each font type. When we return NULL the default - // font will be created. - gfx::Font* test_font = NULL; - EXPECT_CALL(delegate, GetFontMock(_)) - .Times(8) - .WillRepeatedly(Return(test_font)); - - const gfx::FontList* font_list = - &resource_bundle->GetFontList(ui::ResourceBundle::BaseFont); - EXPECT_TRUE(font_list); - - const gfx::Font* font = - &resource_bundle->GetFont(ui::ResourceBundle::BaseFont); - EXPECT_TRUE(font); -} - -#if defined(OS_CHROMEOS) && defined(USE_PANGO) -TEST_F(ResourceBundleTest, FontListReload) { - MockResourceBundleDelegate delegate; - ResourceBundle* resource_bundle = CreateResourceBundle(&delegate); - - // Should be called once for each font type. When we return NULL the default - // font will be created. - gfx::Font* test_font = nullptr; - EXPECT_CALL(delegate, GetFontMock(_)) - .Times(16) - .WillRepeatedly(Return(test_font)); - - EXPECT_CALL(delegate, GetLocalizedStringMock(IDS_UI_FONT_FAMILY_CROS)) - .WillOnce(Return(base::UTF8ToUTF16("test font, 12px"))); - resource_bundle->ReloadFonts(); - // Don't test the font name; it'll get mapped to something else by Fontconfig. - EXPECT_EQ(12, gfx::FontList().GetPrimaryFont().GetFontSize()); - EXPECT_EQ(gfx::Font::NORMAL, gfx::FontList().GetPrimaryFont().GetStyle()); - - EXPECT_CALL(delegate, GetLocalizedStringMock(IDS_UI_FONT_FAMILY_CROS)) - .WillOnce(Return(base::UTF8ToUTF16("test font 2, Bold 10px"))); - resource_bundle->ReloadFonts(); - EXPECT_EQ(10, gfx::FontList().GetPrimaryFont().GetFontSize()); - EXPECT_EQ(gfx::Font::BOLD, gfx::FontList().GetPrimaryFont().GetStyle()); -} -#endif - TEST_F(ResourceBundleTest, LocaleDataPakExists) { ResourceBundle* resource_bundle = CreateResourceBundle(NULL); diff --git a/chromium/ui/base/resource/resource_bundle_win.cc b/chromium/ui/base/resource/resource_bundle_win.cc index 6792870be96..4546b66fcd7 100644 --- a/chromium/ui/base/resource/resource_bundle_win.cc +++ b/chromium/ui/base/resource/resource_bundle_win.cc @@ -37,10 +37,7 @@ void ResourceBundle::LoadCommonResources() { LoadChromeResources(); } -gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { - // Flipped image is not used on Windows. - DCHECK_EQ(rtl, RTL_DISABLED); - +gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { // Windows only uses SkBitmap for gfx::Image, so this is the same as // GetImageNamed. return GetImageNamed(resource_id); diff --git a/chromium/ui/base/resource/resource_handle.h b/chromium/ui/base/resource/resource_handle.h index 138c739134c..9632c536713 100644 --- a/chromium/ui/base/resource/resource_handle.h +++ b/chromium/ui/base/resource/resource_handle.h @@ -8,8 +8,8 @@ #include <stdint.h> #include "base/strings/string_piece.h" -#include "ui/base/layout.h" -#include "ui/base/ui_base_export.h" +#include "ui/base/resource/data_pack_export.h" +#include "ui/base/resource/scale_factor.h" namespace base { class RefCountedStaticMemory; @@ -17,7 +17,7 @@ class RefCountedStaticMemory; namespace ui { -class UI_BASE_EXPORT ResourceHandle { +class UI_DATA_PACK_EXPORT ResourceHandle { public: // What type of encoding the text resources use. enum TextEncodingType { diff --git a/chromium/ui/base/resource/scale_factor.cc b/chromium/ui/base/resource/scale_factor.cc new file mode 100644 index 00000000000..dceffaec424 --- /dev/null +++ b/chromium/ui/base/resource/scale_factor.cc @@ -0,0 +1,24 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/resource/scale_factor.h" + +#include "base/macros.h" + +namespace ui { + +namespace { + +const float kScaleFactorScales[] = {1.0f, 1.0f, 1.25f, 1.33f, 1.4f, 1.5f, 1.8f, + 2.0f, 2.5f, 3.0f}; +static_assert(NUM_SCALE_FACTORS == arraysize(kScaleFactorScales), + "kScaleFactorScales has incorrect size"); + +} // namespace + +float GetScaleForScaleFactor(ScaleFactor scale_factor) { + return kScaleFactorScales[scale_factor]; +} + +} // namespace ui diff --git a/chromium/ui/base/resource/scale_factor.h b/chromium/ui/base/resource/scale_factor.h new file mode 100644 index 00000000000..06205851715 --- /dev/null +++ b/chromium/ui/base/resource/scale_factor.h @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_RESOURCE_SCALE_FACTOR_H_ +#define UI_BASE_RESOURCE_SCALE_FACTOR_H_ + +#include "ui/base/resource/data_pack_export.h" + +namespace ui { + +// Supported UI scale factors for the platform. This is used as an index +// into the array |kScaleFactorScales| which maps the enum value to a float. +// SCALE_FACTOR_NONE is used for density independent resources such as +// string, html/js files or an image that can be used for any scale factors +// (such as wallpapers). +enum ScaleFactor : int { + SCALE_FACTOR_NONE = 0, + SCALE_FACTOR_100P, + SCALE_FACTOR_125P, + SCALE_FACTOR_133P, + SCALE_FACTOR_140P, + SCALE_FACTOR_150P, + SCALE_FACTOR_180P, + SCALE_FACTOR_200P, + SCALE_FACTOR_250P, + SCALE_FACTOR_300P, + + NUM_SCALE_FACTORS // This always appears last. +}; + +// Returns the image scale for the scale factor passed in. +UI_DATA_PACK_EXPORT float GetScaleForScaleFactor(ScaleFactor scale_factor); + +} // namespace ui + +#endif // UI_BASE_RESOURCE_SCALE_FACTOR_H_ diff --git a/chromium/ui/base/resource/scale_factor_unittest.cc b/chromium/ui/base/resource/scale_factor_unittest.cc new file mode 100644 index 00000000000..52bf39b2ab8 --- /dev/null +++ b/chromium/ui/base/resource/scale_factor_unittest.cc @@ -0,0 +1,23 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/resource/scale_factor.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace ui { + +TEST(ScaleFactorTest, GetScaleFactorScale) { + EXPECT_FLOAT_EQ(1.0f, GetScaleForScaleFactor(SCALE_FACTOR_100P)); + EXPECT_FLOAT_EQ(1.25f, GetScaleForScaleFactor(SCALE_FACTOR_125P)); + EXPECT_FLOAT_EQ(1.33f, GetScaleForScaleFactor(SCALE_FACTOR_133P)); + EXPECT_FLOAT_EQ(1.4f, GetScaleForScaleFactor(SCALE_FACTOR_140P)); + EXPECT_FLOAT_EQ(1.5f, GetScaleForScaleFactor(SCALE_FACTOR_150P)); + EXPECT_FLOAT_EQ(1.8f, GetScaleForScaleFactor(SCALE_FACTOR_180P)); + EXPECT_FLOAT_EQ(2.0f, GetScaleForScaleFactor(SCALE_FACTOR_200P)); + EXPECT_FLOAT_EQ(2.5f, GetScaleForScaleFactor(SCALE_FACTOR_250P)); + EXPECT_FLOAT_EQ(3.0f, GetScaleForScaleFactor(SCALE_FACTOR_300P)); +} + +} // namespace ui diff --git a/chromium/ui/base/template_expressions.cc b/chromium/ui/base/template_expressions.cc index 1d63e0c34ea..10aaf4fecf5 100644 --- a/chromium/ui/base/template_expressions.cc +++ b/chromium/ui/base/template_expressions.cc @@ -7,37 +7,69 @@ #include <stddef.h> #include "base/logging.h" +#include "net/base/escape.h" + +namespace { +const char kLeader[] = "$i18n"; +const size_t kLeaderSize = arraysize(kLeader) - 1; +const char kKeyOpen = '{'; +const char kKeyClose = '}'; +} // namespace namespace ui { std::string ReplaceTemplateExpressions( - base::StringPiece format_string, - const std::map<base::StringPiece, std::string>& substitutions) { + base::StringPiece source, + const TemplateReplacements& replacements) { std::string formatted; const size_t kValueLengthGuess = 16; - formatted.reserve(format_string.length() + - substitutions.size() * kValueLengthGuess); - base::StringPiece::const_iterator i = format_string.begin(); - while (i < format_string.end()) { - if (*i == '$' && i + 2 < format_string.end() && i[1] == '{' && - i[2] != '}') { - size_t key_start = i + strlen("${") - format_string.begin(); - size_t key_length = format_string.find('}', key_start); - if (key_length == base::StringPiece::npos) - NOTREACHED() << "TemplateExpression missing ending brace '}'"; - key_length -= key_start; - base::StringPiece key(format_string.begin() + key_start, key_length); - const auto& replacement = substitutions.find(key); - if (replacement != substitutions.end()) { - formatted.append(replacement->second); - i += strlen("${") + key_length + strlen("}"); - continue; - } else { - NOTREACHED() << "TemplateExpression key not found: " << key; - } + formatted.reserve(source.length() + replacements.size() * kValueLengthGuess); + // Two position markers are used as cursors through the |source|. + // The |current_pos| will follow behind |next_pos|. + size_t current_pos = 0; + while (true) { + size_t next_pos = source.find(kLeader, current_pos); + + if (next_pos == std::string::npos) { + source.substr(current_pos).AppendToString(&formatted); + break; } - formatted.push_back(*i); - ++i; + + source.substr(current_pos, next_pos - current_pos) + .AppendToString(&formatted); + current_pos = next_pos + kLeaderSize; + + size_t context_end = source.find(kKeyOpen, current_pos); + CHECK_NE(context_end, std::string::npos); + std::string context; + source.substr(current_pos, context_end - current_pos) + .AppendToString(&context); + current_pos = context_end + sizeof(kKeyOpen); + + size_t key_end = source.find(kKeyClose, current_pos); + CHECK_NE(key_end, std::string::npos); + + std::string key = + source.substr(current_pos, key_end - current_pos).as_string(); + CHECK(!key.empty()); + + TemplateReplacements::const_iterator value = replacements.find(key); + CHECK(value != replacements.end()) << "$i18n replacement key \"" << key + << "\" not found"; + + std::string replacement = value->second; + if (context.empty()) { + // Make the replacement HTML safe. + replacement = net::EscapeForHTML(replacement); + } else if (context == "Raw") { + // Pass the replacement through unchanged. + } else { + CHECK(false) << "Unknown context " << context; + } + + formatted.append(replacement); + + current_pos = key_end + sizeof(kKeyClose); } return formatted; } diff --git a/chromium/ui/base/template_expressions.h b/chromium/ui/base/template_expressions.h index ff14ee1b719..2531d96af20 100644 --- a/chromium/ui/base/template_expressions.h +++ b/chromium/ui/base/template_expressions.h @@ -16,12 +16,15 @@ namespace ui { -// Replace ${foo} in the format string with the value for the foo key in +// Map of strings for template replacement in |ReplaceTemplateExpressions|. +typedef std::map<const std::string, std::string> TemplateReplacements; + +// Replace $i18n*{foo} in the format string with the value for the foo key in // |subst|. If the key is not found in the |substitutions| that item will // be unaltered. UI_BASE_EXPORT std::string ReplaceTemplateExpressions( - base::StringPiece format_string, - const std::map<base::StringPiece, std::string>& substitutions); + base::StringPiece source, + const TemplateReplacements& replacements); } // namespace ui diff --git a/chromium/ui/base/template_expressions_unittest.cc b/chromium/ui/base/template_expressions_unittest.cc index b56a5e7424c..c219d0c7db9 100644 --- a/chromium/ui/base/template_expressions_unittest.cc +++ b/chromium/ui/base/template_expressions_unittest.cc @@ -9,27 +9,49 @@ namespace ui { TEST(TemplateExpressionsTest, ReplaceTemplateExpressionsPieces) { - std::map<base::StringPiece, std::string> substitutions; + TemplateReplacements substitutions; substitutions["test"] = "word"; substitutions["5"] = "number"; - EXPECT_EQ("${}", ReplaceTemplateExpressions("${}", substitutions)); EXPECT_EQ("", ReplaceTemplateExpressions("", substitutions)); - EXPECT_EQ("word", ReplaceTemplateExpressions("${test}", substitutions)); - EXPECT_EQ("number ", ReplaceTemplateExpressions("${5} ", substitutions)); - EXPECT_EQ( - "multiple: word, number.", - ReplaceTemplateExpressions("multiple: ${test}, ${5}.", substitutions)); + EXPECT_EQ("word", ReplaceTemplateExpressions("$i18n{test}", substitutions)); + EXPECT_EQ("number ", ReplaceTemplateExpressions("$i18n{5} ", substitutions)); + EXPECT_EQ("multiple: word, number.", + ReplaceTemplateExpressions("multiple: $i18n{test}, $i18n{5}.", + substitutions)); } TEST(TemplateExpressionsTest, ReplaceTemplateExpressionsConsecutiveDollarSignsPieces) { - std::map<base::StringPiece, std::string> substitutions; + TemplateReplacements substitutions; substitutions["a"] = "x"; EXPECT_EQ("$ $$ $$$", ReplaceTemplateExpressions("$ $$ $$$", substitutions)); - EXPECT_EQ("$x", ReplaceTemplateExpressions("$${a}", substitutions)); - EXPECT_EQ("$$x", ReplaceTemplateExpressions("$$${a}", substitutions)); - EXPECT_EQ("$12", ReplaceTemplateExpressions("$12", substitutions)); + EXPECT_EQ("$x", ReplaceTemplateExpressions("$$i18n{a}", substitutions)); + EXPECT_EQ("$$x", ReplaceTemplateExpressions("$$$i18n{a}", substitutions)); + EXPECT_EQ("$i1812", ReplaceTemplateExpressions("$i1812", substitutions)); +} + +TEST(TemplateExpressionsTest, ReplaceTemplateExpressionsEscaping) { + static TemplateReplacements substitutions; + substitutions["punctuationSample"] = "a\"b'c<d>e&f"; + substitutions["htmlSample"] = "<div>hello</div>"; + EXPECT_EQ( + "a"b'c<d>e&f", + ReplaceTemplateExpressions("$i18n{punctuationSample}", substitutions)); + EXPECT_EQ("<div>hello</div>", + ReplaceTemplateExpressions("$i18n{htmlSample}", substitutions)); + EXPECT_EQ( + "multiple: <div>hello</div>, a"b'c<d>e&f.", + ReplaceTemplateExpressions( + "multiple: $i18n{htmlSample}, $i18n{punctuationSample}.", + substitutions)); +} + +TEST(TemplateExpressionsTest, ReplaceTemplateExpressionsRaw) { + static TemplateReplacements substitutions; + substitutions["rawSample"] = "<a href=\"example.com\">hello</a>"; + EXPECT_EQ("<a href=\"example.com\">hello</a>", + ReplaceTemplateExpressions("$i18nRaw{rawSample}", substitutions)); } } // namespace ui diff --git a/chromium/ui/base/theme_provider.h b/chromium/ui/base/theme_provider.h index fa86e43ede0..ce98246b051 100644 --- a/chromium/ui/base/theme_provider.h +++ b/chromium/ui/base/theme_provider.h @@ -80,9 +80,15 @@ class UI_BASE_EXPORT ThemeProvider { // ThemeProvider, but it's used in many places on OSX. virtual bool UsingSystemTheme() const = 0; + // Returns whether or not theme is in Incognito mode. + virtual bool InIncognitoMode() const = 0; + // Gets the NSImage with the specified |id|. virtual NSImage* GetNSImageNamed(int id) const = 0; + // Returns true if the theme has defined a custom color for color |id|. + virtual bool HasCustomColor(int id) const = 0; + // Gets the NSImage that GetNSImageNamed (above) would return, but returns it // as a pattern color. virtual NSColor* GetNSImageColorNamed(int id) const = 0; diff --git a/chromium/ui/base/ui_base.gyp b/chromium/ui/base/ui_base.gyp index 565faed3fe6..1eda3ca1e3f 100644 --- a/chromium/ui/base/ui_base.gyp +++ b/chromium/ui/base/ui_base.gyp @@ -8,10 +8,56 @@ }, 'targets': [ { + # GN version: //ui/base:ui_data_pack + # As part of building Chrome on iOS, it is necessary to run a tool on + # the host to load datapack and generate output in a format defined + # by the platform (this is to support notifications). + # + # Introduce a standalone target that build on both 'host' and 'target' + # toolset that just build the support to load datapack. The dependency + # should be kept minimal to have to build too many targets with multiple + # toolsets. + 'target_name': 'ui_data_pack', + 'toolsets': ['host', 'target'], + 'type': '<(component)', + 'dependencies': [ + '../../base/base.gyp:base', + ], + 'sources': [ + 'resource/data_pack.cc', + 'resource/data_pack.h', + 'resource/data_pack_export.h', + 'resource/resource_handle.h', + 'resource/scale_factor.cc', + 'resource/scale_factor.h', + ], + 'defines': [ + 'UI_DATA_PACK_IMPLEMENTATION', + ], + 'conditions': [ + ['OS=="win"', { + # TODO(jschuh): C4267: http://crbug.com/167187 size_t -> int + 'msvs_disabled_warnings': [ 4267 ], + }], + ], + }, + { # GN version: //ui/base:ui_features + 'target_name': 'ui_features', + 'includes': [ '../../build/buildflag_header.gypi' ], + 'variables': { + 'buildflag_header_path': 'ui/base/ui_features.h', + 'buildflag_flags': [ + 'ENABLE_HIDPI=<(enable_hidpi)', + ], + }, + }, + { # GN version: //ui/base 'target_name': 'ui_base', 'type': '<(component)', 'dependencies': [ + 'ui_data_pack', + 'ui_features', '../../base/base.gyp:base', '../../base/base.gyp:base_i18n', '../../base/base.gyp:base_static', @@ -66,6 +112,8 @@ 'clipboard/clipboard_mac.h', 'clipboard/clipboard_mac.mm', 'clipboard/clipboard_types.h', + 'clipboard/clipboard_util_mac.h', + 'clipboard/clipboard_util_mac.mm', 'clipboard/clipboard_util_win.cc', 'clipboard/clipboard_util_win.h', 'clipboard/clipboard_win.cc', @@ -163,6 +211,7 @@ 'default_theme_provider.cc', 'default_theme_provider.h', 'default_theme_provider_mac.mm', + 'default_style.h', 'device_form_factor.h', 'device_form_factor_android.cc', 'device_form_factor_android.h', @@ -230,6 +279,8 @@ 'layout.cc', 'layout.h', 'layout_mac.mm', + 'material_design/material_design_controller.cc', + 'material_design/material_design_controller.h', 'models/button_menu_item_model.cc', 'models/button_menu_item_model.h', 'models/combobox_model.cc', @@ -260,10 +311,6 @@ 'nine_image_painter_factory.h', 'page_transition_types.cc', 'page_transition_types.h', - 'resource/data_pack.cc', - 'resource/data_pack.h', - 'resource/material_design/material_design_controller.cc', - 'resource/material_design/material_design_controller.h', 'resource/resource_bundle.cc', 'resource/resource_bundle.h', 'resource/resource_bundle_android.cc', @@ -274,7 +321,6 @@ 'resource/resource_bundle_win.h', 'resource/resource_data_dll_win.cc', 'resource/resource_data_dll_win.h', - 'resource/resource_handle.h', 'template_expressions.cc', 'template_expressions.h', 'text/bytes_formatting.cc', @@ -373,6 +419,7 @@ ['include', '(^|/)ios/'], ['include', '^l10n/'], ['include', '^layout'], + ['include', '^material_design/'], ['include', '^page_transition_type'], ['include', '^resource/'], ['include', 'template_expressions.cc'], @@ -417,6 +464,7 @@ ['use_ozone==1', { 'dependencies': [ '../events/devices/events_devices.gyp:events_devices', + '../events/ozone/events_ozone.gyp:events_ozone_evdev', '../events/ozone/events_ozone.gyp:events_ozone_layout', '../ozone/ozone.gyp:ozone_base', ], @@ -651,6 +699,7 @@ 'target_name': 'ui_base_test_support', 'type': 'static_library', 'dependencies': [ + 'ui_data_pack', '../../base/base.gyp:base', '../../skia/skia.gyp:skia', '../../testing/gtest.gyp:gtest', @@ -679,7 +728,8 @@ ], 'conditions': [ ['OS!="ios"', { - 'dependecies': [ + 'dependencies': [ + '../events/events.gyp:events', 'ime/ui_base_ime.gyp:ui_base_ime', ], 'sources': [ @@ -693,6 +743,8 @@ 'test/scoped_fake_nswindow_focus.mm', 'test/scoped_fake_nswindow_fullscreen.h', 'test/scoped_fake_nswindow_fullscreen.mm', + 'test/scoped_preferred_scroller_style_mac.h', + 'test/scoped_preferred_scroller_style_mac.mm', 'test/windowed_nsnotification_observer.h', 'test/windowed_nsnotification_observer.mm', ], diff --git a/chromium/ui/base/ui_base_switches.cc b/chromium/ui/base/ui_base_switches.cc index 76609e1f4a4..7ec3c6bf649 100644 --- a/chromium/ui/base/ui_base_switches.cc +++ b/chromium/ui/base/ui_base_switches.cc @@ -79,11 +79,7 @@ const char kTopChromeMDMaterial[] = "material"; const char kTopChromeMDMaterialHybrid[] = "material-hybrid"; // Classic, non-material, mode for the |kTopChromeMD| switch. -const char kTopChromeMDNonMaterial[] = ""; +const char kTopChromeMDNonMaterial[] = "non-material"; #endif // defined(ENABLE_TOPCHROME_MD) -// On Windows only: requests that Chrome connect to the running Metro viewer -// process. -const char kViewerConnect[] = "connect-to-metro-viewer"; - } // namespace switches diff --git a/chromium/ui/base/ui_base_switches.h b/chromium/ui/base/ui_base_switches.h index e0c5849e147..5c4f8a91076 100644 --- a/chromium/ui/base/ui_base_switches.h +++ b/chromium/ui/base/ui_base_switches.h @@ -44,8 +44,6 @@ UI_BASE_EXPORT extern const char kTopChromeMDMaterialHybrid[]; UI_BASE_EXPORT extern const char kTopChromeMDNonMaterial[]; #endif // defined(ENABLE_TOPCHROME_MD) -UI_BASE_EXPORT extern const char kViewerConnect[]; - } // namespace switches #endif // UI_BASE_UI_BASE_SWITCHES_H_ diff --git a/chromium/ui/base/ui_base_tests.gyp b/chromium/ui/base/ui_base_tests.gyp index 483b5ae6dec..bf2027a929a 100644 --- a/chromium/ui/base/ui_base_tests.gyp +++ b/chromium/ui/base/ui_base_tests.gyp @@ -28,12 +28,14 @@ '../strings/ui_strings.gyp:ui_strings', 'ime/ui_base_ime.gyp:ui_base_ime', 'ui_base.gyp:ui_base', + 'ui_base.gyp:ui_data_pack', 'ui_base.gyp:ui_base_test_support', ], # iOS uses a small subset of ui. common_sources are the only files that # are built on iOS. 'common_sources' : [ # Note: file list duplicated in GN build. + 'clipboard/clipboard_util_mac_unittest.mm', 'ios/cru_context_menu_controller_unittest.mm', 'l10n/l10n_util_mac_unittest.mm', 'l10n/l10n_util_unittest.cc', @@ -43,8 +45,9 @@ 'models/tree_node_iterator_unittest.cc', 'resource/data_pack_literal.cc', 'resource/data_pack_unittest.cc', - 'resource/resource_bundle_unittest.cc', 'resource/resource_bundle_mac_unittest.mm', + 'resource/resource_bundle_unittest.cc', + 'resource/scale_factor_unittest.cc', 'template_expressions_unittest.cc', 'test/run_all_unittests.cc', ], @@ -72,21 +75,20 @@ 'cocoa/tracking_area_unittest.mm', 'dragdrop/os_exchange_data_provider_aurax11_unittest.cc', 'ime/candidate_window_unittest.cc', - 'ime/composition_text_unittest.cc', 'ime/chromeos/character_composer_unittest.cc', + 'ime/composition_text_unittest.cc', 'ime/composition_text_util_pango_unittest.cc', 'ime/input_method_base_unittest.cc', 'ime/input_method_chromeos_unittest.cc', - 'ime/remote_input_method_win_unittest.cc', 'ime/win/imm32_manager_unittest.cc', 'ime/win/tsf_input_scope_unittest.cc', + 'material_design/material_design_controller_unittest.cc', 'models/list_model_unittest.cc', 'models/list_selection_model_unittest.cc', 'models/tree_node_model_unittest.cc', - 'resource/material_design/material_design_controller_unittest.cc', 'test/data/resource.h', - 'test/test_clipboard_unittest.cc', 'test/scoped_fake_nswindow_fullscreen_unittest.mm', + 'test/test_clipboard_unittest.cc', 'text/bytes_formatting_unittest.cc', 'touch/selection_bound_unittest.cc', 'user_activity/user_activity_detector_unittest.cc', @@ -166,13 +168,6 @@ 'dependencies': [ '../../build/linux/system.gyp:pangocairo', ], - 'conditions': [ - ['use_allocator!="none"', { - 'dependencies': [ - '../../base/allocator/allocator.gyp:allocator', - ], - }], - ], }], ['use_x11==1', { 'dependencies': [ diff --git a/chromium/ui/base/ui_base_types.h b/chromium/ui/base/ui_base_types.h index 4ed9f25cfd5..4d29bb30d8c 100644 --- a/chromium/ui/base/ui_base_types.h +++ b/chromium/ui/base/ui_base_types.h @@ -47,7 +47,9 @@ enum MenuSourceType { MENU_SOURCE_KEYBOARD = 2, MENU_SOURCE_TOUCH = 3, MENU_SOURCE_TOUCH_EDIT_MENU = 4, - MENU_SOURCE_TYPE_LAST = MENU_SOURCE_TOUCH_EDIT_MENU + MENU_SOURCE_LONG_PRESS = 5, + MENU_SOURCE_LONG_TAP = 6, + MENU_SOURCE_TYPE_LAST = MENU_SOURCE_LONG_TAP }; UI_BASE_EXPORT MenuSourceType GetMenuSourceTypeForEvent(const ui::Event& event); diff --git a/chromium/ui/base/ui_base_unittests_apk.isolate b/chromium/ui/base/ui_base_unittests_apk.isolate index 33fc4811634..87a76282f90 100644 --- a/chromium/ui/base/ui_base_unittests_apk.isolate +++ b/chromium/ui/base/ui_base_unittests_apk.isolate @@ -9,6 +9,7 @@ 'variables': { 'command': [ '<(PRODUCT_DIR)/bin/run_ui_base_unittests', + '--logcat-output-dir', '${ISOLATED_OUTDIR}/logcats', ], 'files': [ '../../third_party/icu/icu.isolate', diff --git a/chromium/ui/base/ui_features.gni b/chromium/ui/base/ui_features.gni new file mode 100644 index 00000000000..724c4d44968 --- /dev/null +++ b/chromium/ui/base/ui_features.gni @@ -0,0 +1,5 @@ +# 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. + +enable_hidpi = is_mac || is_win || is_linux diff --git a/chromium/ui/base/user_activity/user_activity_detector.cc b/chromium/ui/base/user_activity/user_activity_detector.cc index bffe4796c9e..4f04b8623b7 100644 --- a/chromium/ui/base/user_activity/user_activity_detector.cc +++ b/chromium/ui/base/user_activity/user_activity_detector.cc @@ -53,9 +53,7 @@ UserActivityDetector::UserActivityDetector() { ui::PlatformEventSource* platform_event_source = ui::PlatformEventSource::GetInstance(); -#if defined(OS_CHROMEOS) || defined(OS_LINUX) - CHECK(platform_event_source); -#endif + // TODO(sad): Need a PES for mus. if (platform_event_source) platform_event_source->AddPlatformEventObserver(this); } @@ -63,9 +61,6 @@ UserActivityDetector::UserActivityDetector() { UserActivityDetector::~UserActivityDetector() { ui::PlatformEventSource* platform_event_source = ui::PlatformEventSource::GetInstance(); -#if defined(OS_CHROMEOS) || defined(OS_LINUX) - CHECK(platform_event_source); -#endif if (platform_event_source) platform_event_source->RemovePlatformEventObserver(this); g_instance = nullptr; diff --git a/chromium/ui/base/webui/web_ui_util.cc b/chromium/ui/base/webui/web_ui_util.cc index d9c1d76da66..0f91f3f7045 100644 --- a/chromium/ui/base/webui/web_ui_util.cc +++ b/chromium/ui/base/webui/web_ui_util.cc @@ -91,9 +91,10 @@ bool ParseScaleFactor(const base::StringPiece& identifier, void ParsePathAndScale(const GURL& url, std::string* path, float* scale_factor) { - *path = net::UnescapeURLComponent(url.path().substr(1), - (net::UnescapeRule::URL_SPECIAL_CHARS | - net::UnescapeRule::SPACES)); + *path = net::UnescapeURLComponent( + url.path().substr(1), + net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS | + net::UnescapeRule::SPACES); if (scale_factor) *scale_factor = 1.0f; @@ -123,7 +124,7 @@ void SetLoadTimeDataDefaults(const std::string& app_locale, } std::string GetWebUiCssTextDefaults(const std::string& css_template) { - std::map<base::StringPiece, std::string> placeholders; + ui::TemplateReplacements placeholders; placeholders["textDirection"] = GetTextDirection(); placeholders["fontFamily"] = GetFontFamily(); placeholders["fontSize"] = GetFontSize(); diff --git a/chromium/ui/base/win/open_file_name_win.cc b/chromium/ui/base/win/open_file_name_win.cc index 5c2df415a23..8b32ae20303 100644 --- a/chromium/ui/base/win/open_file_name_win.cc +++ b/chromium/ui/base/win/open_file_name_win.cc @@ -86,15 +86,15 @@ OpenFileName::~OpenFileName() { } void OpenFileName::SetFilters( - const std::vector<base::Tuple<base::string16, base::string16>>& filters) { + const std::vector<std::tuple<base::string16, base::string16>>& filters) { openfilename_.lpstrFilter = NULL; filter_buffer_.clear(); if (filters.empty()) return; for (const auto& filter : filters) { - filter_buffer_.append(base::get<0>(filter)); + filter_buffer_.append(std::get<0>(filter)); filter_buffer_.push_back(0); - filter_buffer_.append(base::get<1>(filter)); + filter_buffer_.append(std::get<1>(filter)); filter_buffer_.push_back(0); } filter_buffer_.push_back(0); @@ -203,9 +203,9 @@ void OpenFileName::SetResult(const base::FilePath& directory, } // static -std::vector<base::Tuple<base::string16, base::string16>> +std::vector<std::tuple<base::string16, base::string16>> OpenFileName::GetFilters(const OPENFILENAME* openfilename) { - std::vector<base::Tuple<base::string16, base::string16>> filters; + std::vector<std::tuple<base::string16, base::string16>> filters; const base::char16* display_string = openfilename->lpstrFilter; if (!display_string) @@ -220,7 +220,7 @@ OpenFileName::GetFilters(const OPENFILENAME* openfilename) { while (*pattern_end) ++pattern_end; filters.push_back( - base::MakeTuple(base::string16(display_string, display_string_end), + std::make_tuple(base::string16(display_string, display_string_end), base::string16(pattern, pattern_end))); display_string = pattern_end + 1; } diff --git a/chromium/ui/base/win/open_file_name_win.h b/chromium/ui/base/win/open_file_name_win.h index 596b1470f5f..5d0b028edbb 100644 --- a/chromium/ui/base/win/open_file_name_win.h +++ b/chromium/ui/base/win/open_file_name_win.h @@ -8,11 +8,11 @@ #include <Windows.h> #include <Commdlg.h> +#include <tuple> #include <vector> #include "base/macros.h" #include "base/strings/string16.h" -#include "base/tuple.h" #include "ui/base/ui_base_export.h" namespace base { @@ -34,7 +34,7 @@ class UI_BASE_EXPORT OpenFileName { // Initializes |lpstrFilter| from the label/pattern pairs in |filters|. void SetFilters( - const std::vector<base::Tuple<base::string16, base::string16>>& filters); + const std::vector<std::tuple<base::string16, base::string16>>& filters); // Sets |lpstrInitialDir| and |lpstrFile|. void SetInitialSelection(const base::FilePath& initial_directory, @@ -68,7 +68,7 @@ class UI_BASE_EXPORT OpenFileName { // Returns a vector of label/pattern pairs built from // |openfilename->lpstrFilter|. - static std::vector<base::Tuple<base::string16, base::string16>> GetFilters( + static std::vector<std::tuple<base::string16, base::string16>> GetFilters( const OPENFILENAME* openfilename); private: diff --git a/chromium/ui/base/win/open_file_name_win_unittest.cc b/chromium/ui/base/win/open_file_name_win_unittest.cc index 36de27faa96..bce8abcfa72 100644 --- a/chromium/ui/base/win/open_file_name_win_unittest.cc +++ b/chromium/ui/base/win/open_file_name_win_unittest.cc @@ -30,8 +30,8 @@ void SetResult(const base::string16& result, ui::win::OpenFileName* ofn) { } void CheckFilters( - const std::vector<base::Tuple<base::string16, base::string16>>& expected, - const std::vector<base::Tuple<base::string16, base::string16>>& actual) { + const std::vector<std::tuple<base::string16, base::string16>>& expected, + const std::vector<std::tuple<base::string16, base::string16>>& actual) { if (expected.size() != actual.size()) { ADD_FAILURE() << "filter count mismatch. Got " << actual.size() << " expected " << expected.size() << "."; @@ -39,9 +39,9 @@ void CheckFilters( } for (size_t i = 0; i < expected.size(); ++i) { - EXPECT_EQ(base::get<0>(expected[i]), base::get<0>(actual[i])) + EXPECT_EQ(std::get<0>(expected[i]), std::get<0>(actual[i])) << "Mismatch at index " << i; - EXPECT_EQ(base::get<1>(expected[i]), base::get<1>(actual[i])) + EXPECT_EQ(std::get<1>(expected[i]), std::get<1>(actual[i])) << "Mismatch at index " << i; } } @@ -206,19 +206,21 @@ TEST(OpenFileNameTest, SetAndGetFilters) { const base::string16 kNull(L"\0", 1); ui::win::OpenFileName ofn(kHwnd, kFlags); - std::vector<base::Tuple<base::string16, base::string16>> filters; + std::vector<std::tuple<base::string16, base::string16>> filters; ofn.SetFilters(filters); EXPECT_FALSE(ofn.GetOPENFILENAME()->lpstrFilter); CheckFilters(filters, ui::win::OpenFileName::GetFilters(ofn.GetOPENFILENAME())); - filters.push_back(base::MakeTuple(base::string16(L"a"), base::string16(L"b"))); + filters.push_back( + std::make_tuple(base::string16(L"a"), base::string16(L"b"))); ofn.SetFilters(filters); CheckFilterString(L"a" + kNull + L"b" + kNull, ofn); CheckFilters(filters, ui::win::OpenFileName::GetFilters(ofn.GetOPENFILENAME())); - filters.push_back(base::MakeTuple(base::string16(L"X"), base::string16(L"Y"))); + filters.push_back( + std::make_tuple(base::string16(L"X"), base::string16(L"Y"))); ofn.SetFilters(filters); CheckFilterString(L"a" + kNull + L"b" + kNull + L"X" + kNull + L"Y" + kNull, ofn); diff --git a/chromium/ui/base/win/shell.cc b/chromium/ui/base/win/shell.cc index 8dacd14ce4e..cdc0a8da185 100644 --- a/chromium/ui/base/win/shell.cc +++ b/chromium/ui/base/win/shell.cc @@ -15,6 +15,7 @@ #include "base/files/file_path.h" #include "base/native_library.h" #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" @@ -114,7 +115,8 @@ bool PreventWindowFromPinning(HWND hwnd) { // TODO(calamity): investigate moving this out of the UI thread as COM // operations may spawn nested message loops which can cause issues. void SetAppDetailsForWindow(const base::string16& app_id, - const base::string16& app_icon, + const base::FilePath& app_icon_path, + int app_icon_index, const base::string16& relaunch_command, const base::string16& relaunch_display_name, HWND hwnd) { @@ -131,9 +133,14 @@ void SetAppDetailsForWindow(const base::string16& app_id, if (!app_id.empty()) base::win::SetAppIdForPropertyStore(pps.get(), app_id.c_str()); - if (!app_icon.empty()) { + if (!app_icon_path.empty()) { + // Always add the icon index explicitly to prevent bad interaction with the + // index notation when file path has commas. base::win::SetStringValueForPropertyStore( - pps.get(), PKEY_AppUserModel_RelaunchIconResource, app_icon.c_str()); + pps.get(), PKEY_AppUserModel_RelaunchIconResource, + base::StringPrintf(L"%ls,%d", app_icon_path.value().c_str(), + app_icon_index) + .c_str()); } if (!relaunch_command.empty()) { base::win::SetStringValueForPropertyStore( @@ -148,29 +155,22 @@ void SetAppDetailsForWindow(const base::string16& app_id, } void SetAppIdForWindow(const base::string16& app_id, HWND hwnd) { - SetAppDetailsForWindow(app_id, - base::string16(), - base::string16(), - base::string16(), - hwnd); + SetAppDetailsForWindow(app_id, base::FilePath(), 0, base::string16(), + base::string16(), hwnd); } -void SetAppIconForWindow(const base::string16& app_icon, HWND hwnd) { - SetAppDetailsForWindow(base::string16(), - app_icon, - base::string16(), - base::string16(), - hwnd); +void SetAppIconForWindow(const base::FilePath& app_icon_path, + int app_icon_index, + HWND hwnd) { + SetAppDetailsForWindow(base::string16(), app_icon_path, app_icon_index, + base::string16(), base::string16(), hwnd); } void SetRelaunchDetailsForWindow(const base::string16& relaunch_command, const base::string16& display_name, HWND hwnd) { - SetAppDetailsForWindow(base::string16(), - base::string16(), - relaunch_command, - display_name, - hwnd); + SetAppDetailsForWindow(base::string16(), base::FilePath(), 0, + relaunch_command, display_name, hwnd); } void ClearWindowPropertyStore(HWND hwnd) { diff --git a/chromium/ui/base/win/shell.h b/chromium/ui/base/win/shell.h index 458074c6ff9..df784a6e13b 100644 --- a/chromium/ui/base/win/shell.h +++ b/chromium/ui/base/win/shell.h @@ -51,10 +51,12 @@ UI_BASE_EXPORT bool OpenAnyViaShell(const base::string16& full_path, UI_BASE_EXPORT bool PreventWindowFromPinning(HWND hwnd); // Sets the application id, app icon, relaunch command and relaunch display name -// for the given window. +// for the given window. |app_icon_index| should be set to 0 if the app icon +// file only has a single icon. UI_BASE_EXPORT void SetAppDetailsForWindow( const base::string16& app_id, - const base::string16& app_icon, + const base::FilePath& app_icon_path, + int app_icon_index, const base::string16& relaunch_command, const base::string16& relaunch_display_name, HWND hwnd); @@ -65,7 +67,8 @@ UI_BASE_EXPORT void SetAppDetailsForWindow( UI_BASE_EXPORT void SetAppIdForWindow(const base::string16& app_id, HWND hwnd); // Sets the application icon for the window specified. -UI_BASE_EXPORT void SetAppIconForWindow(const base::string16& app_icon, +UI_BASE_EXPORT void SetAppIconForWindow(const base::FilePath& app_icon_path, + int app_icon_index, HWND hwnd); // Sets the relaunch command and relaunch display name for the window specified. diff --git a/chromium/ui/base/x/selection_owner.cc b/chromium/ui/base/x/selection_owner.cc index d13f088d5aa..170f32e19b2 100644 --- a/chromium/ui/base/x/selection_owner.cc +++ b/chromium/ui/base/x/selection_owner.cc @@ -376,6 +376,9 @@ SelectionOwner::IncrementalTransfer::IncrementalTransfer( foreign_window_manager_id(foreign_window_manager_id) { } +SelectionOwner::IncrementalTransfer::IncrementalTransfer( + const IncrementalTransfer& other) = default; + SelectionOwner::IncrementalTransfer::~IncrementalTransfer() { } diff --git a/chromium/ui/base/x/selection_owner.h b/chromium/ui/base/x/selection_owner.h index f3bec33aca8..6ae933aada2 100644 --- a/chromium/ui/base/x/selection_owner.h +++ b/chromium/ui/base/x/selection_owner.h @@ -67,6 +67,7 @@ class UI_BASE_EXPORT SelectionOwner { int offset, base::TimeTicks timeout, int foreign_window_manager_id); + IncrementalTransfer(const IncrementalTransfer& other); ~IncrementalTransfer(); // Parameters from the XSelectionRequest. The data is transferred over diff --git a/chromium/ui/base/x/selection_requestor.cc b/chromium/ui/base/x/selection_requestor.cc index 977de29f4b8..d4e13d808e0 100644 --- a/chromium/ui/base/x/selection_requestor.cc +++ b/chromium/ui/base/x/selection_requestor.cc @@ -54,8 +54,7 @@ scoped_refptr<base::RefCountedMemory> CombineRefCountedMemory( data[i]->front(), data[i]->front() + data[i]->size()); } - return scoped_refptr<base::RefCountedMemory>( - base::RefCountedBytes::TakeVector(&combined_data)); + return base::RefCountedBytes::TakeVector(&combined_data); } } // namespace diff --git a/chromium/ui/base/x/selection_utils.cc b/chromium/ui/base/x/selection_utils.cc index af84c4babbe..d7acd52c029 100644 --- a/chromium/ui/base/x/selection_utils.cc +++ b/chromium/ui/base/x/selection_utils.cc @@ -6,6 +6,7 @@ #include <stdint.h> +#include <algorithm> #include <set> #include "base/i18n/icu_string_conversions.h" @@ -120,6 +121,9 @@ base::string16 RefCountedMemoryToString16( SelectionFormatMap::SelectionFormatMap() {} +SelectionFormatMap::SelectionFormatMap(const SelectionFormatMap& other) = + default; + SelectionFormatMap::~SelectionFormatMap() {} void SelectionFormatMap::Insert( diff --git a/chromium/ui/base/x/selection_utils.h b/chromium/ui/base/x/selection_utils.h index 1c4bcbdbc8c..addbcf7db21 100644 --- a/chromium/ui/base/x/selection_utils.h +++ b/chromium/ui/base/x/selection_utils.h @@ -66,6 +66,7 @@ class UI_BASE_EXPORT SelectionFormatMap { typedef InternalMap::const_iterator const_iterator; SelectionFormatMap(); + SelectionFormatMap(const SelectionFormatMap& other); ~SelectionFormatMap(); // Copy and assignment deliberately open. diff --git a/chromium/ui/compositor/BUILD.gn b/chromium/ui/compositor/BUILD.gn index ff3d4d3f108..7f849b7846a 100644 --- a/chromium/ui/compositor/BUILD.gn +++ b/chromium/ui/compositor/BUILD.gn @@ -48,6 +48,7 @@ component("compositor") { "layer_delegate.h", "layer_owner.cc", "layer_owner.h", + "layer_threaded_animation_delegate.h", "layer_tree_owner.cc", "layer_tree_owner.h", "layer_type.h", @@ -110,6 +111,9 @@ source_set("test_support") { "test/layer_animation_observer_test_api.h", "test/layer_animator_test_controller.cc", "test/layer_animator_test_controller.h", + "test/multi_layer_animator_test_controller.cc", + "test/multi_layer_animator_test_controller.h", + "test/multi_layer_animator_test_controller_delegate.h", "test/test_compositor_host.h", "test/test_compositor_host_android.cc", "test/test_compositor_host_mac.mm", @@ -159,15 +163,6 @@ source_set("test_support") { } } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("compositor_unittests_run") { - testonly = true - deps = [ - ":compositor_unittests", - ] -} - test("compositor_unittests") { sources = [ "callback_layer_animation_observer_unittest.cc", @@ -189,10 +184,11 @@ test("compositor_unittests") { ":compositor", ":test_support", "//base", - "//base/allocator", "//base/test:test_support", "//cc", "//cc:test_support", + "//cc/surfaces", + "//cc/surfaces:surface_id", "//skia", "//testing/gmock", "//testing/gtest", diff --git a/chromium/ui/compositor/compositing_recorder.cc b/chromium/ui/compositor/compositing_recorder.cc index 842cc3a2ec3..fd255c731ef 100644 --- a/chromium/ui/compositor/compositing_recorder.cc +++ b/chromium/ui/compositor/compositing_recorder.cc @@ -23,7 +23,7 @@ CompositingRecorder::CompositingRecorder(const PaintContext& context, context_.list_->CreateAndAppendItem<cc::CompositingDisplayItem>( bounds_in_layer_, alpha, SkXfermode::kSrcOver_Mode, - nullptr /* no bounds */, skia::RefPtr<SkColorFilter>(), + nullptr /* no bounds */, nullptr /* no color filter */, lcd_text_requires_opaque_layer); } diff --git a/chromium/ui/compositor/compositor.cc b/chromium/ui/compositor/compositor.cc index afb502601aa..9c492698b5c 100644 --- a/chromium/ui/compositor/compositor.cc +++ b/chromium/ui/compositor/compositor.cc @@ -87,7 +87,7 @@ Compositor::Compositor(ui::ContextFactory* context_factory, compositor_lock_(NULL), layer_animator_collection_(this), weak_ptr_factory_(this) { - root_web_layer_ = cc::Layer::Create(Layer::UILayerSettings()); + root_web_layer_ = cc::Layer::Create(); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); @@ -126,6 +126,8 @@ Compositor::Compositor(ui::ContextFactory* context_factory, // These flags should be mirrored by renderer versions in content/renderer/. settings.initial_debug_state.show_debug_borders = command_line->HasSwitch(cc::switches::kUIShowCompositedLayerBorders); + settings.initial_debug_state.show_fps_counter = + command_line->HasSwitch(cc::switches::kUIShowFPSCounter); settings.initial_debug_state.show_layer_animation_bounds_rects = command_line->HasSwitch(cc::switches::kUIShowLayerAnimationBounds); settings.initial_debug_state.show_paint_rects = @@ -142,12 +144,10 @@ Compositor::Compositor(ui::ContextFactory* context_factory, settings.initial_debug_state.SetRecordRenderingStats( command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking)); - if (command_line->HasSwitch(cc::switches::kDisableCompositorPropertyTrees)) - settings.use_property_trees = false; settings.use_zero_copy = IsUIZeroCopyEnabled(); - settings.renderer_settings.use_rgba_4444_textures = - command_line->HasSwitch(switches::kUIEnableRGBA4444Textures); + if (command_line->HasSwitch(switches::kUIEnableRGBA4444Textures)) + settings.renderer_settings.preferred_tile_format = cc::RGBA_4444; // UI compositor always uses partial raster if not using zero-copy. Zero copy // doesn't currently support partial raster. @@ -172,9 +172,6 @@ Compositor::Compositor(ui::ContextFactory* context_factory, // thread. settings.image_decode_tasks_enabled = false; - settings.use_compositor_animation_timelines = !command_line->HasSwitch( - switches::kUIDisableCompositorAnimationTimelines); - #if !defined(OS_ANDROID) // TODO(sohanjg): Revisit this memory usage in tile manager. cc::ManagedMemoryPolicy policy( @@ -197,11 +194,10 @@ Compositor::Compositor(ui::ContextFactory* context_factory, UMA_HISTOGRAM_TIMES("GPU.CreateBrowserCompositor", base::TimeTicks::Now() - before_create); - if (settings.use_compositor_animation_timelines) { - animation_timeline_ = cc::AnimationTimeline::Create( - cc::AnimationIdProvider::NextTimelineId()); - host_->animation_host()->AddAnimationTimeline(animation_timeline_.get()); - } + animation_timeline_ = + cc::AnimationTimeline::Create(cc::AnimationIdProvider::NextTimelineId()); + host_->animation_host()->AddAnimationTimeline(animation_timeline_.get()); + host_->SetRootLayer(root_web_layer_); host_->set_surface_id_namespace(surface_id_allocator_->id_namespace()); host_->SetVisible(true); @@ -472,6 +468,11 @@ void Compositor::DidAbortSwapBuffers() { OnCompositingAborted(this)); } +void Compositor::SetOutputIsSecure(bool output_is_secure) { + host_->SetOutputIsSecure(output_is_secure); + host_->SetNeedsRedraw(); +} + void Compositor::SendBeginFramesToChildren(const cc::BeginFrameArgs& args) { FOR_EACH_OBSERVER(CompositorBeginFrameObserver, begin_frame_observer_list_, OnSendBeginFrame(args)); diff --git a/chromium/ui/compositor/compositor.gyp b/chromium/ui/compositor/compositor.gyp index 4ab5b0c1146..c692274f84e 100644 --- a/chromium/ui/compositor/compositor.gyp +++ b/chromium/ui/compositor/compositor.gyp @@ -66,6 +66,7 @@ 'layer_delegate.h', 'layer_owner.cc', 'layer_owner.h', + 'layer_threaded_animation_delegate.h', 'layer_tree_owner.cc', 'layer_tree_owner.h', 'layer_type.h', @@ -128,6 +129,9 @@ 'test/layer_animation_observer_test_api.h', 'test/layer_animator_test_controller.cc', 'test/layer_animator_test_controller.h', + 'test/multi_layer_animator_test_controller.cc', + 'test/multi_layer_animator_test_controller.h', + 'test/multi_layer_animator_test_controller_delegate.h', 'test/test_compositor_host.h', 'test/test_compositor_host_android.cc', 'test/test_compositor_host_mac.mm', @@ -161,6 +165,7 @@ '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/base.gyp:test_support_base', '<(DEPTH)/cc/cc.gyp:cc', + '<(DEPTH)/cc/cc.gyp:cc_surfaces', '<(DEPTH)/cc/cc_tests.gyp:cc_test_support', '<(DEPTH)/skia/skia.gyp:skia', '<(DEPTH)/testing/gmock.gyp:gmock', @@ -191,15 +196,6 @@ '<(DEPTH)/third_party/mesa/mesa.gyp:osmesa', ], }], - ['os_posix == 1 and OS != "mac"', { - 'conditions': [ - ['use_allocator!="none"', { - 'dependencies': [ - '<(DEPTH)/base/allocator/allocator.gyp:allocator', - ], - }], - ], - }], ['OS == "android"', { 'dependencies': [ '../../testing/android/native_test.gyp:native_test_native_code', diff --git a/chromium/ui/compositor/compositor.h b/chromium/ui/compositor/compositor.h index 5222b41d956..acae0be40c2 100644 --- a/chromium/ui/compositor/compositor.h +++ b/chromium/ui/compositor/compositor.h @@ -318,6 +318,8 @@ class COMPOSITOR_EXPORT Compositor bool IsLocked() { return compositor_lock_ != NULL; } + void SetOutputIsSecure(bool output_is_secure); + const cc::LayerTreeDebugState& GetLayerTreeDebugState() const; void SetLayerTreeDebugState(const cc::LayerTreeDebugState& debug_state); const cc::RendererSettings& GetRendererSettings() const; diff --git a/chromium/ui/compositor/compositor_switches.cc b/chromium/ui/compositor/compositor_switches.cc index 1a2c6ca55e3..c8464ddfdaa 100644 --- a/chromium/ui/compositor/compositor_switches.cc +++ b/chromium/ui/compositor/compositor_switches.cc @@ -15,9 +15,6 @@ const char kEnableHardwareOverlays[] = "enable-hardware-overlays"; // Forces tests to produce pixel output when they normally wouldn't. const char kEnablePixelOutputInTests[] = "enable-pixel-output-in-tests"; -const char kUIDisableCompositorAnimationTimelines[] = - "ui-disable-compositor-animation-timelines"; - // Disable partial swap which is needed for some OpenGL drivers / emulators. const char kUIDisablePartialSwap[] = "ui-disable-partial-swap"; diff --git a/chromium/ui/compositor/compositor_switches.h b/chromium/ui/compositor/compositor_switches.h index 929722a15f5..62705efd076 100644 --- a/chromium/ui/compositor/compositor_switches.h +++ b/chromium/ui/compositor/compositor_switches.h @@ -11,7 +11,6 @@ namespace switches { COMPOSITOR_EXPORT extern const char kEnableHardwareOverlays[]; COMPOSITOR_EXPORT extern const char kEnablePixelOutputInTests[]; -COMPOSITOR_EXPORT extern const char kUIDisableCompositorAnimationTimelines[]; COMPOSITOR_EXPORT extern const char kUIDisablePartialSwap[]; COMPOSITOR_EXPORT extern const char kUIEnableRGBA4444Textures[]; COMPOSITOR_EXPORT extern const char kUIEnableZeroCopy[]; diff --git a/chromium/ui/compositor/layer.cc b/chromium/ui/compositor/layer.cc index 9503a8a38f6..0f83cb60001 100644 --- a/chromium/ui/compositor/layer.cc +++ b/chromium/ui/compositor/layer.cc @@ -10,19 +10,15 @@ #include "base/auto_reset.h" #include "base/command_line.h" #include "base/json/json_writer.h" -#include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/trace_event/trace_event.h" -#include "cc/layers/delegated_renderer_layer.h" -#include "cc/layers/layer_settings.h" #include "cc/layers/nine_patch_layer.h" #include "cc/layers/picture_layer.h" #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/output/delegated_frame_data.h" #include "cc/output/filter_operation.h" #include "cc/output/filter_operations.h" #include "cc/playback/display_item_list_settings.h" @@ -48,9 +44,6 @@ const ui::Layer* GetRoot(const ui::Layer* layer) { return layer; } -base::LazyInstance<cc::LayerSettings> g_ui_layer_settings = - LAZY_INSTANCE_INITIALIZER; - } // namespace namespace ui { @@ -120,19 +113,8 @@ Layer::~Layer() { children_[i]->parent_ = NULL; cc_layer_->RemoveFromParent(); -} - -// static -const cc::LayerSettings& Layer::UILayerSettings() { - return g_ui_layer_settings.Get(); -} - -// static -void Layer::InitializeUILayerSettings() { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - g_ui_layer_settings.Get().use_compositor_animation_timelines = - !command_line->HasSwitch( - switches::kUIDisableCompositorAnimationTimelines); + if (mailbox_release_callback_) + mailbox_release_callback_->Run(gpu::SyncToken(), false); } const Compositor* Layer::GetCompositor() const { @@ -157,7 +139,6 @@ void Layer::SetCompositor(Compositor* compositor, root_layer->AddChild(cc_layer_); SetCompositorForAnimatorsInTree(compositor); - SendPendingThreadedAnimations(); } void Layer::ResetCompositor() { @@ -177,10 +158,8 @@ void Layer::Add(Layer* child) { cc_layer_->AddChild(child->cc_layer_); child->OnDeviceScaleFactorChanged(device_scale_factor_); Compositor* compositor = GetCompositor(); - if (compositor) { + if (compositor) child->SetCompositorForAnimatorsInTree(compositor); - child->SendPendingThreadedAnimations(); - } } void Layer::Remove(Layer* child) { @@ -505,7 +484,6 @@ void Layer::SwitchToLayer(scoped_refptr<cc::Layer> new_layer) { if (texture_layer_.get()) texture_layer_->ClearClient(); - // TODO(piman): delegated_renderer_layer_ cleanup. cc_layer_->RemoveAllChildren(); if (cc_layer_->parent()) { @@ -521,7 +499,6 @@ void Layer::SwitchToLayer(scoped_refptr<cc::Layer> new_layer) { content_layer_ = NULL; solid_color_layer_ = NULL; texture_layer_ = NULL; - delegated_renderer_layer_ = NULL; surface_layer_ = NULL; for (size_t i = 0; i < children_.size(); ++i) { @@ -539,9 +516,12 @@ void Layer::SwitchToLayer(scoped_refptr<cc::Layer> new_layer) { SetLayerBackgroundFilters(); } +bool Layer::HasPendingThreadedAnimationsForTesting() const { + return animator_->HasPendingThreadedAnimationsForTesting(); +} + void Layer::SwitchCCLayerForTest() { - scoped_refptr<cc::Layer> new_layer = - cc::PictureLayer::Create(UILayerSettings(), this); + scoped_refptr<cc::Layer> new_layer = cc::PictureLayer::Create(this); SwitchToLayer(new_layer); content_layer_ = new_layer; } @@ -555,7 +535,7 @@ void Layer::SetTextureMailbox( DCHECK(release_callback); if (!texture_layer_.get()) { scoped_refptr<cc::TextureLayer> new_layer = - cc::TextureLayer::CreateForMailbox(UILayerSettings(), this); + cc::TextureLayer::CreateForMailbox(this); new_layer->SetFlipped(true); SwitchToLayer(new_layer); texture_layer_ = new_layer; @@ -589,19 +569,6 @@ bool Layer::TextureFlipped() const { return texture_layer_->flipped(); } -void Layer::SetShowDelegatedContent(cc::DelegatedFrameProvider* frame_provider, - gfx::Size frame_size_in_dip) { - DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR); - - scoped_refptr<cc::DelegatedRendererLayer> new_layer = - cc::DelegatedRendererLayer::Create(UILayerSettings(), frame_provider); - SwitchToLayer(new_layer); - delegated_renderer_layer_ = new_layer; - - frame_size_in_dip_ = frame_size_in_dip; - RecomputeDrawsContentAndUVRect(); -} - void Layer::SetShowSurface( cc::SurfaceId surface_id, const cc::SurfaceLayer::SatisfyCallback& satisfy_callback, @@ -611,8 +578,8 @@ void Layer::SetShowSurface( gfx::Size frame_size_in_dip) { DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR); - scoped_refptr<cc::SurfaceLayer> new_layer = cc::SurfaceLayer::Create( - UILayerSettings(), satisfy_callback, require_callback); + scoped_refptr<cc::SurfaceLayer> new_layer = + cc::SurfaceLayer::Create(satisfy_callback, require_callback); new_layer->SetSurfaceId(surface_id, scale, surface_size); SwitchToLayer(new_layer); surface_layer_ = new_layer; @@ -627,8 +594,7 @@ void Layer::SetShowSolidColorContent() { if (solid_color_layer_.get()) return; - scoped_refptr<cc::SolidColorLayer> new_layer = - cc::SolidColorLayer::Create(UILayerSettings()); + scoped_refptr<cc::SolidColorLayer> new_layer = cc::SolidColorLayer::Create(); SwitchToLayer(new_layer); solid_color_layer_ = new_layer; @@ -751,7 +717,7 @@ void Layer::OnDeviceScaleFactorChanged(float device_scale_factor) { } void Layer::OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) { - DCHECK(delegated_renderer_layer_.get() || surface_layer_.get()); + DCHECK(surface_layer_.get()); if (!delegate_) return; delegate_->OnDelegatedFrameDamage(damage_rect_in_dip); @@ -814,6 +780,7 @@ void Layer::SetForceRenderSurface(bool force) { class LayerDebugInfo : public base::trace_event::ConvertableToTraceFormat { public: explicit LayerDebugInfo(const std::string& name) : name_(name) {} + ~LayerDebugInfo() override {} void AppendAsTraceFormat(std::string* out) const override { base::DictionaryValue dictionary; dictionary.SetString("layer_name", name_); @@ -821,13 +788,12 @@ class LayerDebugInfo : public base::trace_event::ConvertableToTraceFormat { } private: - ~LayerDebugInfo() override {} std::string name_; }; -scoped_refptr<base::trace_event::ConvertableToTraceFormat> Layer::TakeDebugInfo( +scoped_ptr<base::trace_event::ConvertableToTraceFormat> Layer::TakeDebugInfo( cc::Layer* layer) { - return new LayerDebugInfo(name_); + return make_scoped_ptr(new LayerDebugInfo(name_)); } void Layer::CollectAnimators( @@ -981,44 +947,6 @@ float Layer::GetDeviceScaleFactor() const { return device_scale_factor_; } -void Layer::AddThreadedAnimation(scoped_ptr<cc::Animation> animation) { - DCHECK(cc_layer_); - // Until this layer has a compositor (and hence cc_layer_ has a - // LayerTreeHost), addAnimation will fail. - if (GetCompositor()) { - if (UILayerSettings().use_compositor_animation_timelines) { - DCHECK(animator_); - animator_->AddThreadedAnimation(std::move(animation)); - } else { - cc_layer_->AddAnimation(std::move(animation)); - } - } else { - pending_threaded_animations_.push_back(std::move(animation)); - } -} - -void Layer::RemoveThreadedAnimation(int animation_id) { - DCHECK(cc_layer_); - if (pending_threaded_animations_.size() == 0) { - if (UILayerSettings().use_compositor_animation_timelines) { - DCHECK(animator_); - animator_->RemoveThreadedAnimation(animation_id); - } else { - cc_layer_->RemoveAnimation(animation_id); - } - return; - } - - pending_threaded_animations_.erase( - std::remove_if( - pending_threaded_animations_.begin(), - pending_threaded_animations_.end(), - [animation_id](const scoped_ptr<cc::Animation>& animation) { - return animation->id() == animation_id; - }), - pending_threaded_animations_.end()); -} - LayerAnimatorCollection* Layer::GetLayerAnimatorCollection() { Compositor* compositor = GetCompositor(); return compositor ? compositor->layer_animator_collection() : NULL; @@ -1028,30 +956,20 @@ cc::Layer* Layer::GetCcLayer() const { return cc_layer_; } -void Layer::SendPendingThreadedAnimations() { - for (auto& animation : pending_threaded_animations_) { - if (UILayerSettings().use_compositor_animation_timelines) { - DCHECK(animator_); - animator_->AddThreadedAnimation(std::move(animation)); - } else { - cc_layer_->AddAnimation(std::move(animation)); - } - } - pending_threaded_animations_.clear(); - - for (auto* child : children_) - child->SendPendingThreadedAnimations(); +LayerThreadedAnimationDelegate* Layer::GetThreadedAnimationDelegate() { + DCHECK(animator_); + return animator_.get(); } void Layer::CreateCcLayer() { if (type_ == LAYER_SOLID_COLOR) { - solid_color_layer_ = cc::SolidColorLayer::Create(UILayerSettings()); + solid_color_layer_ = cc::SolidColorLayer::Create(); cc_layer_ = solid_color_layer_.get(); } else if (type_ == LAYER_NINE_PATCH) { - nine_patch_layer_ = cc::NinePatchLayer::Create(UILayerSettings()); + nine_patch_layer_ = cc::NinePatchLayer::Create(); cc_layer_ = nine_patch_layer_.get(); } else { - content_layer_ = cc::PictureLayer::Create(UILayerSettings(), this); + content_layer_ = cc::PictureLayer::Create(this); cc_layer_ = content_layer_.get(); } cc_layer_->SetTransformOrigin(gfx::Point3F()); @@ -1075,7 +993,7 @@ void Layer::RecomputeDrawsContentAndUVRect() { static_cast<float>(size.width()) / frame_size_in_dip_.width(), static_cast<float>(size.height()) / frame_size_in_dip_.height()); texture_layer_->SetUV(uv_top_left, uv_bottom_right); - } else if (delegated_renderer_layer_.get() || surface_layer_.get()) { + } else if (surface_layer_.get()) { size.SetToMin(frame_size_in_dip_); } cc_layer_->SetBounds(size); @@ -1106,8 +1024,7 @@ void Layer::ResetCompositorForAnimatorsInTree(Compositor* compositor) { if (animator_) { animator_->ResetCompositor(compositor); - if (animator_->is_animating()) - animator_->RemoveFromCollection(collection); + animator_->RemoveFromCollection(collection); } for (auto* child : children_) diff --git a/chromium/ui/compositor/layer.h b/chromium/ui/compositor/layer.h index 689cf39a370..2dcb9c765be 100644 --- a/chromium/ui/compositor/layer.h +++ b/chromium/ui/compositor/layer.h @@ -37,8 +37,6 @@ class SkCanvas; namespace cc { class ContentLayer; class CopyOutputRequest; -class DelegatedFrameProvider; -class DelegatedRendererLayer; class Layer; class NinePatchLayer; class ResourceUpdateQueue; @@ -54,6 +52,7 @@ namespace ui { class Compositor; class LayerAnimator; class LayerOwner; +class LayerThreadedAnimationDelegate; // Layer manages a texture, transform and a set of child Layers. Any View that // has enabled layers ends up creating a Layer to manage the texture. @@ -75,9 +74,6 @@ class COMPOSITOR_EXPORT Layer explicit Layer(LayerType type); ~Layer() override; - static const cc::LayerSettings& UILayerSettings(); - static void InitializeUILayerSettings(); - // Retrieves the Layer's compositor. The Layer will walk up its parent chain // to locate it. Returns NULL if the Layer is not attached to a compositor. Compositor* GetCompositor() { @@ -284,10 +280,6 @@ class COMPOSITOR_EXPORT Layer void SetTextureFlipped(bool flipped); bool TextureFlipped() const; - // Begins showing delegated frames from the |frame_provider|. - void SetShowDelegatedContent(cc::DelegatedFrameProvider* frame_provider, - gfx::Size frame_size_in_dip); - // Begins showing content from a surface with a particular id. void SetShowSurface(cc::SurfaceId surface_id, const cc::SurfaceLayer::SatisfyCallback& satisfy_callback, @@ -297,8 +289,7 @@ class COMPOSITOR_EXPORT Layer gfx::Size frame_size_in_dip); bool has_external_content() { - return texture_layer_.get() || delegated_renderer_layer_.get() || - surface_layer_.get(); + return texture_layer_.get() || surface_layer_.get(); } // Show a solid color instead of delegated or surface contents. @@ -369,13 +360,11 @@ class COMPOSITOR_EXPORT Layer bool force_render_surface() const { return force_render_surface_; } // LayerClient - scoped_refptr<base::trace_event::ConvertableToTraceFormat> TakeDebugInfo( + scoped_ptr<base::trace_event::ConvertableToTraceFormat> TakeDebugInfo( cc::Layer* layer) override; // Whether this layer has animations waiting to get sent to its cc::Layer. - bool HasPendingThreadedAnimations() { - return pending_threaded_animations_.size() != 0; - } + bool HasPendingThreadedAnimationsForTesting() const; // Triggers a call to SwitchToLayer. void SwitchCCLayerForTest(); @@ -409,10 +398,9 @@ class COMPOSITOR_EXPORT Layer float GetGrayscaleForAnimation() const override; SkColor GetColorForAnimation() const override; float GetDeviceScaleFactor() const override; - void AddThreadedAnimation(scoped_ptr<cc::Animation> animation) override; - void RemoveThreadedAnimation(int animation_id) override; - LayerAnimatorCollection* GetLayerAnimatorCollection() override; cc::Layer* GetCcLayer() const override; + LayerThreadedAnimationDelegate* GetThreadedAnimationDelegate() override; + LayerAnimatorCollection* GetLayerAnimatorCollection() override; // Creates a corresponding composited layer for |type_|. void CreateCcLayer(); @@ -430,12 +418,6 @@ class COMPOSITOR_EXPORT Layer // Cleanup |cc_layer_| and replaces it with |new_layer|. void SwitchToLayer(scoped_refptr<cc::Layer> new_layer); - // We cannot send animations to our cc_layer_ until we have been added to a - // layer tree. Instead, we hold on to these animations in - // pending_threaded_animations_, and expect SendPendingThreadedAnimations to - // be called once we have been added to a tree. - void SendPendingThreadedAnimations(); - void SetCompositorForAnimatorsInTree(Compositor* compositor); void ResetCompositorForAnimatorsInTree(Compositor* compositor); @@ -497,17 +479,12 @@ class COMPOSITOR_EXPORT Layer scoped_refptr<LayerAnimator> animator_; - // Animations that are passed to AddThreadedAnimation before this layer is - // added to a tree. - std::vector<scoped_ptr<cc::Animation>> pending_threaded_animations_; - // Ownership of the layer is held through one of the strongly typed layer // pointers, depending on which sort of layer this is. scoped_refptr<cc::Layer> content_layer_; scoped_refptr<cc::NinePatchLayer> nine_patch_layer_; scoped_refptr<cc::TextureLayer> texture_layer_; scoped_refptr<cc::SolidColorLayer> solid_color_layer_; - scoped_refptr<cc::DelegatedRendererLayer> delegated_renderer_layer_; scoped_refptr<cc::SurfaceLayer> surface_layer_; cc::Layer* cc_layer_; diff --git a/chromium/ui/compositor/layer_animation_delegate.h b/chromium/ui/compositor/layer_animation_delegate.h index e1ec62451ca..b1db0b300b2 100644 --- a/chromium/ui/compositor/layer_animation_delegate.h +++ b/chromium/ui/compositor/layer_animation_delegate.h @@ -5,8 +5,6 @@ #ifndef UI_COMPOSITOR_LAYER_ANIMATION_DELEGATE_H_ #define UI_COMPOSITOR_LAYER_ANIMATION_DELEGATE_H_ -#include "base/memory/scoped_ptr.h" -#include "cc/animation/animation.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/compositor/compositor_export.h" #include "ui/gfx/geometry/rect.h" @@ -19,6 +17,7 @@ class Layer; namespace ui { class LayerAnimatorCollection; +class LayerThreadedAnimationDelegate; // Layer animations interact with the layers using this interface. class COMPOSITOR_EXPORT LayerAnimationDelegate { @@ -39,14 +38,9 @@ class COMPOSITOR_EXPORT LayerAnimationDelegate { virtual float GetGrayscaleForAnimation() const = 0; virtual SkColor GetColorForAnimation() const = 0; virtual float GetDeviceScaleFactor() const = 0; - - // TODO(loyso): Extract these 3 methods as a separate - // LayerThreadedAnimationDelegate. - virtual void AddThreadedAnimation(scoped_ptr<cc::Animation> animation) = 0; - virtual void RemoveThreadedAnimation(int animation_id) = 0; virtual cc::Layer* GetCcLayer() const = 0; - virtual LayerAnimatorCollection* GetLayerAnimatorCollection() = 0; + virtual LayerThreadedAnimationDelegate* GetThreadedAnimationDelegate() = 0; protected: virtual ~LayerAnimationDelegate() {} diff --git a/chromium/ui/compositor/layer_animation_element.cc b/chromium/ui/compositor/layer_animation_element.cc index 5e0f558f5c9..edf5bdc89fd 100644 --- a/chromium/ui/compositor/layer_animation_element.cc +++ b/chromium/ui/compositor/layer_animation_element.cc @@ -344,7 +344,10 @@ class ThreadedLayerAnimationElement : public LayerAnimationElement { return false; if (Started() && IsThreaded()) { - delegate->RemoveThreadedAnimation(animation_id()); + LayerThreadedAnimationDelegate* threaded = + delegate->GetThreadedAnimationDelegate(); + DCHECK(threaded); + threaded->RemoveThreadedAnimation(animation_id()); } OnEnd(delegate); @@ -353,7 +356,10 @@ class ThreadedLayerAnimationElement : public LayerAnimationElement { void OnAbort(LayerAnimationDelegate* delegate) override { if (delegate && Started() && IsThreaded()) { - delegate->RemoveThreadedAnimation(animation_id()); + LayerThreadedAnimationDelegate* threaded = + delegate->GetThreadedAnimationDelegate(); + DCHECK(threaded); + threaded->RemoveThreadedAnimation(animation_id()); } } @@ -366,7 +372,11 @@ class ThreadedLayerAnimationElement : public LayerAnimationElement { set_effective_start_time(base::TimeTicks()); scoped_ptr<cc::Animation> animation = CreateCCAnimation(); animation->set_needs_synchronized_start_time(true); - delegate->AddThreadedAnimation(std::move(animation)); + + LayerThreadedAnimationDelegate* threaded = + delegate->GetThreadedAnimationDelegate(); + DCHECK(threaded); + threaded->AddThreadedAnimation(std::move(animation)); } virtual void OnEnd(LayerAnimationDelegate* delegate) = 0; @@ -413,9 +423,9 @@ class ThreadedOpacityTransition : public ThreadedLayerAnimationElement { start_, target_, duration())); - scoped_ptr<cc::Animation> animation( - cc::Animation::Create(std::move(animation_curve), animation_id(), - animation_group_id(), cc::Animation::OPACITY)); + scoped_ptr<cc::Animation> animation(cc::Animation::Create( + std::move(animation_curve), animation_id(), animation_group_id(), + cc::TargetProperty::OPACITY)); return animation; } @@ -466,9 +476,9 @@ class ThreadedTransformTransition : public ThreadedLayerAnimationElement { start_, target_, duration())); - scoped_ptr<cc::Animation> animation( - cc::Animation::Create(std::move(animation_curve), animation_id(), - animation_group_id(), cc::Animation::TRANSFORM)); + scoped_ptr<cc::Animation> animation(cc::Animation::Create( + std::move(animation_curve), animation_id(), animation_group_id(), + cc::TargetProperty::TRANSFORM)); return animation; } @@ -538,9 +548,9 @@ class InverseTransformTransition : public ThreadedLayerAnimationElement { } scoped_ptr<cc::Animation> CreateCCAnimation() override { - scoped_ptr<cc::Animation> animation( - cc::Animation::Create(animation_curve_->Clone(), animation_id(), - animation_group_id(), cc::Animation::TRANSFORM)); + scoped_ptr<cc::Animation> animation(cc::Animation::Create( + animation_curve_->Clone(), animation_id(), animation_group_id(), + cc::TargetProperty::TRANSFORM)); return animation; } @@ -732,12 +742,11 @@ void LayerAnimationElement::RequestEffectiveStart( // static LayerAnimationElement::AnimatableProperty -LayerAnimationElement::ToAnimatableProperty( - cc::Animation::TargetProperty property) { +LayerAnimationElement::ToAnimatableProperty(cc::TargetProperty::Type property) { switch (property) { - case cc::Animation::TRANSFORM: + case cc::TargetProperty::TRANSFORM: return TRANSFORM; - case cc::Animation::OPACITY: + case cc::TargetProperty::OPACITY: return OPACITY; default: NOTREACHED(); diff --git a/chromium/ui/compositor/layer_animation_element.h b/chromium/ui/compositor/layer_animation_element.h index dc08f9001fc..111f25478e0 100644 --- a/chromium/ui/compositor/layer_animation_element.h +++ b/chromium/ui/compositor/layer_animation_element.h @@ -45,7 +45,7 @@ class COMPOSITOR_EXPORT LayerAnimationElement { }; static AnimatableProperty ToAnimatableProperty( - cc::Animation::TargetProperty property); + cc::TargetProperty::Type property); struct COMPOSITOR_EXPORT TargetValue { TargetValue(); diff --git a/chromium/ui/compositor/layer_animation_sequence_unittest.cc b/chromium/ui/compositor/layer_animation_sequence_unittest.cc index 708cc0ad8f2..0a62ddb8d5b 100644 --- a/chromium/ui/compositor/layer_animation_sequence_unittest.cc +++ b/chromium/ui/compositor/layer_animation_sequence_unittest.cc @@ -94,7 +94,7 @@ TEST(LayerAnimationSequenceTest, SingleThreadedElement) { effective_start = start_time + delta; sequence.OnThreadedAnimationStarted(cc::AnimationEvent( cc::AnimationEvent::STARTED, 0, sequence.animation_group_id(), - cc::Animation::OPACITY, effective_start)); + cc::TargetProperty::OPACITY, effective_start)); sequence.Progress(effective_start + delta/2, &delegate); EXPECT_FLOAT_EQ(middle, sequence.last_progressed_fraction()); EXPECT_TRUE(sequence.IsFinished(effective_start + delta)); @@ -148,7 +148,7 @@ TEST(LayerAnimationSequenceTest, MultipleElement) { EXPECT_EQ(starting_group_id, sequence.animation_group_id()); sequence.OnThreadedAnimationStarted(cc::AnimationEvent( cc::AnimationEvent::STARTED, 0, sequence.animation_group_id(), - cc::Animation::OPACITY, opacity_effective_start)); + cc::TargetProperty::OPACITY, opacity_effective_start)); sequence.Progress(opacity_effective_start + delta/2, &delegate); EXPECT_FLOAT_EQ(0.5, sequence.last_progressed_fraction()); sequence.Progress(opacity_effective_start + delta, &delegate); @@ -176,7 +176,7 @@ TEST(LayerAnimationSequenceTest, MultipleElement) { EXPECT_NE(starting_group_id, sequence.animation_group_id()); sequence.OnThreadedAnimationStarted(cc::AnimationEvent( cc::AnimationEvent::STARTED, 0, sequence.animation_group_id(), - cc::Animation::TRANSFORM, transform_effective_start)); + cc::TargetProperty::TRANSFORM, transform_effective_start)); sequence.Progress(transform_effective_start + delta/2, &delegate); EXPECT_FLOAT_EQ(0.5, sequence.last_progressed_fraction()); EXPECT_TRUE(sequence.IsFinished(transform_effective_start + delta)); diff --git a/chromium/ui/compositor/layer_animator.cc b/chromium/ui/compositor/layer_animator.cc index 0f902453ae7..265a568ef45 100644 --- a/chromium/ui/compositor/layer_animator.cc +++ b/chromium/ui/compositor/layer_animator.cc @@ -10,11 +10,13 @@ #include "base/memory/scoped_ptr.h" #include "base/trace_event/trace_event.h" #include "cc/animation/animation_events.h" +#include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" #include "cc/animation/animation_player.h" +#include "cc/animation/animation_registrar.h" #include "cc/animation/animation_timeline.h" #include "cc/animation/element_animations.h" -#include "cc/layers/layer_settings.h" +#include "cc/animation/layer_animation_controller.h" #include "cc/output/begin_frame_args.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" @@ -54,10 +56,8 @@ LayerAnimator::LayerAnimator(base::TimeDelta transition_duration) is_started_(false), disable_timer_for_test_(false), adding_animations_(false) { - if (Layer::UILayerSettings().use_compositor_animation_timelines) { - animation_player_ = - cc::AnimationPlayer::Create(cc::AnimationIdProvider::NextPlayerId()); - } + animation_player_ = + cc::AnimationPlayer::Create(cc::AnimationIdProvider::NextPlayerId()); } LayerAnimator::~LayerAnimator() { @@ -67,6 +67,7 @@ LayerAnimator::~LayerAnimator() { } ClearAnimationsInternal(); delegate_ = NULL; + DCHECK(!animation_player_->animation_timeline()); } // static @@ -136,46 +137,58 @@ void LayerAnimator::SetDelegate(LayerAnimationDelegate* delegate) { } void LayerAnimator::SwitchToLayer(scoped_refptr<cc::Layer> new_layer) { - if (delegate_) { - if (animation_player_) - DetachLayerFromAnimationPlayer(); - else - delegate_->GetCcLayer()->RemoveLayerAnimationEventObserver(this); - } - if (new_layer) { - if (animation_player_) - AttachLayerToAnimationPlayer(new_layer->id()); - else - new_layer->AddLayerAnimationEventObserver(this); - } + // Release LAC state for old layer. + animation_controller_state_ = nullptr; + + if (delegate_) + DetachLayerFromAnimationPlayer(); + if (new_layer) + AttachLayerToAnimationPlayer(new_layer->id()); } void LayerAnimator::SetCompositor(Compositor* compositor) { DCHECK(compositor); - if (animation_player_) { - cc::AnimationTimeline* timeline = compositor->GetAnimationTimeline(); - DCHECK(timeline); - timeline->AttachPlayer(animation_player_); - DCHECK(delegate_->GetCcLayer()); - AttachLayerToAnimationPlayer(delegate_->GetCcLayer()->id()); + cc::AnimationTimeline* timeline = compositor->GetAnimationTimeline(); + DCHECK(timeline); + + DCHECK(delegate_->GetCcLayer()); + + // Register LAC so ElementAnimations picks it up via + // AnimationRegistrar::GetAnimationControllerForId. + if (animation_controller_state_) { + DCHECK_EQ(animation_controller_state_->id(), + delegate_->GetCcLayer()->id()); + timeline->animation_host() + ->animation_registrar() + ->RegisterAnimationController(animation_controller_state_.get()); } + + timeline->AttachPlayer(animation_player_); + + AttachLayerToAnimationPlayer(delegate_->GetCcLayer()->id()); + + // Release LAC (it is referenced in ElementAnimations). + animation_controller_state_ = nullptr; } void LayerAnimator::ResetCompositor(Compositor* compositor) { DCHECK(compositor); - if (animation_player_) { - DetachLayerFromAnimationPlayer(); - cc::AnimationTimeline* timeline = compositor->GetAnimationTimeline(); - DCHECK(timeline); - timeline->DetachPlayer(animation_player_); + // Store a reference to LAC if any so it may be picked up in SetCompositor. + if (animation_player_->element_animations()) { + animation_controller_state_ = + animation_player_->element_animations()->layer_animation_controller(); } + + DetachLayerFromAnimationPlayer(); + + cc::AnimationTimeline* timeline = compositor->GetAnimationTimeline(); + DCHECK(timeline); + timeline->DetachPlayer(animation_player_); } void LayerAnimator::AttachLayerToAnimationPlayer(int layer_id) { - DCHECK(animation_player_); - if (!animation_player_->layer_id()) animation_player_->AttachLayer(layer_id); else @@ -189,8 +202,6 @@ void LayerAnimator::AttachLayerToAnimationPlayer(int layer_id) { } void LayerAnimator::DetachLayerFromAnimationPlayer() { - DCHECK(animation_player_); - if (animation_player_->element_animations()) { animation_player_->element_animations() ->layer_animation_controller() @@ -202,15 +213,17 @@ void LayerAnimator::DetachLayerFromAnimationPlayer() { } void LayerAnimator::AddThreadedAnimation(scoped_ptr<cc::Animation> animation) { - DCHECK(animation_player_); animation_player_->AddAnimation(std::move(animation)); } void LayerAnimator::RemoveThreadedAnimation(int animation_id) { - DCHECK(animation_player_); animation_player_->RemoveAnimation(animation_id); } +bool LayerAnimator::HasPendingThreadedAnimationsForTesting() const { + return animation_player_->has_pending_animations_for_testing(); +} + cc::AnimationPlayer* LayerAnimator::GetAnimationPlayerForTesting() const { return animation_player_.get(); } @@ -440,7 +453,7 @@ void LayerAnimator::AddToCollection(LayerAnimatorCollection* collection) { } void LayerAnimator::RemoveFromCollection(LayerAnimatorCollection* collection) { - if (is_animating() && is_started_) { + if (is_started_) { collection->StopAnimator(this); is_started_ = false; } @@ -951,6 +964,9 @@ LayerAnimator::RunningAnimation::RunningAnimation( : sequence_(sequence) { } +LayerAnimator::RunningAnimation::RunningAnimation( + const RunningAnimation& other) = default; + LayerAnimator::RunningAnimation::~RunningAnimation() { } } // namespace ui diff --git a/chromium/ui/compositor/layer_animator.h b/chromium/ui/compositor/layer_animator.h index dc37edcbdce..48e8a1e0ab4 100644 --- a/chromium/ui/compositor/layer_animator.h +++ b/chromium/ui/compositor/layer_animator.h @@ -18,6 +18,7 @@ #include "cc/animation/layer_animation_event_observer.h" #include "ui/compositor/compositor_export.h" #include "ui/compositor/layer_animation_element.h" +#include "ui/compositor/layer_threaded_animation_delegate.h" #include "ui/gfx/animation/tween.h" namespace cc { @@ -25,6 +26,7 @@ class Animation; class AnimationPlayer; class AnimationTimeline; class Layer; +class LayerAnimationController; } namespace gfx { @@ -53,6 +55,7 @@ class ScopedLayerAnimationSettings; // must guarantee that |this| is valid. class COMPOSITOR_EXPORT LayerAnimator : public base::RefCounted<LayerAnimator>, + public LayerThreadedAnimationDelegate, NON_EXPORTED_BASE(public cc::LayerAnimationEventObserver) { public: enum PreemptionStrategy { @@ -117,11 +120,8 @@ class COMPOSITOR_EXPORT LayerAnimator // Detach AnimationPlayer from Layer and AnimationTimeline void ResetCompositor(Compositor* compositor); - // TODO(loyso): Rework it as an implementation for - // LayerThreadedAnimationDelegate and make it private. - void AddThreadedAnimation(scoped_ptr<cc::Animation> animation); - void RemoveThreadedAnimation(int animation_id); - + // Whether this animator has animations waiting to get sent to cc::LAC. + bool HasPendingThreadedAnimationsForTesting() const; cc::AnimationPlayer* GetAnimationPlayerForTesting() const; // Sets the animation preemption strategy. This determines the behaviour if @@ -245,6 +245,7 @@ class COMPOSITOR_EXPORT LayerAnimator class RunningAnimation { public: RunningAnimation(const base::WeakPtr<LayerAnimationSequence>& sequence); + RunningAnimation(const RunningAnimation& other); ~RunningAnimation(); bool is_sequence_alive() const { return !!sequence_.get(); } @@ -341,6 +342,10 @@ class COMPOSITOR_EXPORT LayerAnimator // LayerAnimationEventObserver void OnAnimationStarted(const cc::AnimationEvent& event) override; + // Implementation of LayerThreadedAnimationDelegate. + void AddThreadedAnimation(scoped_ptr<cc::Animation> animation) override; + void RemoveThreadedAnimation(int animation_id) override; + void AttachLayerToAnimationPlayer(int layer_id); void DetachLayerFromAnimationPlayer(); @@ -387,6 +392,11 @@ class COMPOSITOR_EXPORT LayerAnimator // aborted. base::ObserverList<LayerAnimationObserver> observers_; + // We store a state of LayerAnimationController here to save it in + // ResetCompositor/SetCompositor scope. + // TODO(loyso): Remove it. crbug.com/592873. + scoped_refptr<cc::LayerAnimationController> animation_controller_state_; + DISALLOW_COPY_AND_ASSIGN(LayerAnimator); }; diff --git a/chromium/ui/compositor/layer_animator_unittest.cc b/chromium/ui/compositor/layer_animator_unittest.cc index 30f008b7459..b4afb64cd25 100644 --- a/chromium/ui/compositor/layer_animator_unittest.cc +++ b/chromium/ui/compositor/layer_animator_unittest.cc @@ -375,7 +375,7 @@ TEST(LayerAnimatorTest, ScheduleThreadedAnimationThatCanRunImmediately) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, effective_start)); + cc::TargetProperty::OPACITY, effective_start)); animator->Step(effective_start + delta / 2); @@ -484,7 +484,7 @@ TEST(LayerAnimatorTest, ScheduleThreadedAndNonThreadedAnimations) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, effective_start)); + cc::TargetProperty::OPACITY, effective_start)); animator->Step(effective_start + delta / 2); @@ -746,7 +746,7 @@ TEST(LayerAnimatorTest, StartThreadedAnimationThatCanRunImmediately) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, effective_start)); + cc::TargetProperty::OPACITY, effective_start)); animator->Step(effective_start + delta / 2); @@ -870,7 +870,7 @@ TEST(LayerAnimatorTest, PreemptThreadedByImmediatelyAnimatingToNewTarget) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, effective_start)); + cc::TargetProperty::OPACITY, effective_start)); animator->Step(effective_start + delta / 2); @@ -893,7 +893,7 @@ TEST(LayerAnimatorTest, PreemptThreadedByImmediatelyAnimatingToNewTarget) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, second_effective_start)); + cc::TargetProperty::OPACITY, second_effective_start)); animator->Step(second_effective_start + delta / 2); @@ -1193,7 +1193,7 @@ TEST(LayerAnimatorTest, MultiPreemptThreadedByImmediatelyAnimatingToNewTarget) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, effective_start)); + cc::TargetProperty::OPACITY, effective_start)); animator->Step(effective_start + delta / 2); @@ -1221,7 +1221,7 @@ TEST(LayerAnimatorTest, MultiPreemptThreadedByImmediatelyAnimatingToNewTarget) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, second_effective_start)); + cc::TargetProperty::OPACITY, second_effective_start)); animator->Step(second_effective_start + delta / 2); @@ -1465,7 +1465,7 @@ TEST(LayerAnimatorTest, ThreadedCyclicSequences) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, effective_start)); + cc::TargetProperty::OPACITY, effective_start)); animator->Step(effective_start + delta); EXPECT_TRUE(test_controller.animator()->is_animating()); @@ -1476,7 +1476,7 @@ TEST(LayerAnimatorTest, ThreadedCyclicSequences) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, second_effective_start)); + cc::TargetProperty::OPACITY, second_effective_start)); animator->Step(second_effective_start + delta); @@ -1488,7 +1488,7 @@ TEST(LayerAnimatorTest, ThreadedCyclicSequences) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, third_effective_start)); + cc::TargetProperty::OPACITY, third_effective_start)); animator->Step(third_effective_start + delta); EXPECT_TRUE(test_controller.animator()->is_animating()); @@ -1499,7 +1499,7 @@ TEST(LayerAnimatorTest, ThreadedCyclicSequences) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, fourth_effective_start)); + cc::TargetProperty::OPACITY, fourth_effective_start)); // Skip ahead by a lot. animator->Step(fourth_effective_start + 1000 * delta); @@ -1512,7 +1512,7 @@ TEST(LayerAnimatorTest, ThreadedCyclicSequences) { cc::AnimationEvent::STARTED, 0, test_controller.GetRunningSequence(LayerAnimationElement::OPACITY) ->animation_group_id(), - cc::Animation::OPACITY, fifth_effective_start)); + cc::TargetProperty::OPACITY, fifth_effective_start)); // Skip ahead by a lot. animator->Step(fifth_effective_start + 999 * delta); @@ -2526,21 +2526,62 @@ TEST(LayerAnimatorTest, LayerMovedBetweenCompositorsDuringAnimation) { Layer layer; root_1.Add(&layer); LayerAnimator* animator = layer.GetAnimator(); + EXPECT_FALSE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting()); + double target_opacity = 1.0; base::TimeDelta time_delta = base::TimeDelta::FromSeconds(1); + animator->ScheduleAnimation(new LayerAnimationSequence( LayerAnimationElement::CreateOpacityElement(target_opacity, time_delta))); EXPECT_TRUE(compositor_1->layer_animator_collection()->HasActiveAnimators()); EXPECT_FALSE(compositor_2->layer_animator_collection()->HasActiveAnimators()); + EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting()); root_2.Add(&layer); EXPECT_FALSE(compositor_1->layer_animator_collection()->HasActiveAnimators()); EXPECT_TRUE(compositor_2->layer_animator_collection()->HasActiveAnimators()); + EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting()); + host_2.reset(); host_1.reset(); TerminateContextFactoryForTests(); } +TEST(LayerAnimatorTest, ThreadedAnimationSurvivesIfLayerRemovedAdded) { + bool enable_pixel_output = false; + ui::ContextFactory* context_factory = + InitializeContextFactoryForTests(enable_pixel_output); + const gfx::Rect bounds(10, 10, 100, 100); + scoped_ptr<TestCompositorHost> host( + TestCompositorHost::Create(bounds, context_factory)); + host->Show(); + + Compositor* compositor = host->GetCompositor(); + + Layer root; + compositor->SetRootLayer(&root); + + Layer layer; + root.Add(&layer); + + LayerAnimator* animator = layer.GetAnimator(); + double target_opacity = 1.0; + base::TimeDelta time_delta = base::TimeDelta::FromSeconds(1); + + animator->ScheduleAnimation(new LayerAnimationSequence( + LayerAnimationElement::CreateOpacityElement(target_opacity, time_delta))); + EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting()); + + root.Remove(&layer); + EXPECT_FALSE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting()); + + root.Add(&layer); + EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting()); + + host.reset(); + TerminateContextFactoryForTests(); +} + class LayerOwnerAnimationObserver : public LayerAnimationObserver { public: LayerOwnerAnimationObserver(LayerAnimator* animator) diff --git a/chromium/ui/compositor/layer_owner.cc b/chromium/ui/compositor/layer_owner.cc index c9180810ebd..44bb322ff6c 100644 --- a/chromium/ui/compositor/layer_owner.cc +++ b/chromium/ui/compositor/layer_owner.cc @@ -48,6 +48,8 @@ scoped_ptr<Layer> LayerOwner::RecreateLayer() { new_layer->SetFillsBoundsOpaquely(old_layer->fills_bounds_opaquely()); new_layer->SetFillsBoundsCompletely(old_layer->FillsBoundsCompletely()); new_layer->SetSubpixelPositionOffset(old_layer->subpixel_position_offset()); + new_layer->SetLayerInverted(old_layer->layer_inverted()); + new_layer->SetTransform(old_layer->GetTargetTransform()); if (old_layer->type() == LAYER_SOLID_COLOR) new_layer->SetColor(old_layer->GetTargetColor()); SkRegion* alpha_shape = old_layer->alpha_shape(); diff --git a/chromium/ui/compositor/layer_owner_unittest.cc b/chromium/ui/compositor/layer_owner_unittest.cc index afb6b947323..69216aa9c14 100644 --- a/chromium/ui/compositor/layer_owner_unittest.cc +++ b/chromium/ui/compositor/layer_owner_unittest.cc @@ -7,7 +7,6 @@ #include "base/macros.h" #include "base/test/null_task_runner.h" #include "cc/animation/animation_player.h" -#include "cc/layers/layer_settings.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" @@ -144,6 +143,45 @@ TEST(LayerOwnerTest, RecreateRootLayerWithNullCompositor) { EXPECT_EQ(nullptr, layer_copy->GetCompositor()); } +TEST(LayerOwnerTest, InvertPropertyRemainSameWithRecreateLayer) { + LayerOwner owner; + Layer* layer = new Layer; + owner.SetLayer(layer); + + layer->SetLayerInverted(true); + scoped_ptr<Layer> old_layer1 = owner.RecreateLayer(); + EXPECT_EQ(old_layer1->layer_inverted(), owner.layer()->layer_inverted()); + + old_layer1->SetLayerInverted(false); + scoped_ptr<Layer> old_layer2 = owner.RecreateLayer(); + EXPECT_EQ(old_layer2->layer_inverted(), owner.layer()->layer_inverted()); +} + +TEST(LayerOwnerTest, RecreateLayerWithTransform) { + LayerOwner owner; + Layer* layer = new Layer; + owner.SetLayer(layer); + + gfx::Transform transform; + transform.Scale(2, 1); + transform.Translate(10, 5); + + layer->SetTransform(transform); + + scoped_ptr<Layer> old_layer1 = owner.RecreateLayer(); + // Both new layer and original layer have the same transform. + EXPECT_EQ(transform, old_layer1->GetTargetTransform()); + EXPECT_EQ(transform, owner.layer()->GetTargetTransform()); + + // But they're now separated, so changing the old layer's transform + // should not affect the owner's. + owner.layer()->SetTransform(gfx::Transform()); + EXPECT_EQ(transform, old_layer1->GetTargetTransform()); + scoped_ptr<Layer> old_layer2 = owner.RecreateLayer(); + EXPECT_TRUE(old_layer2->GetTargetTransform().IsIdentity()); + EXPECT_TRUE(owner.layer()->GetTargetTransform().IsIdentity()); +} + TEST_F(LayerOwnerTestWithCompositor, RecreateRootLayerWithCompositor) { LayerOwner owner; Layer* layer = new Layer; @@ -228,10 +266,6 @@ TEST_F(LayerOwnerTestWithCompositor, RecreateNonRootLayerDuringAnimation) { // Tests that if LayerOwner-derived class destroys layer, then // LayerAnimator's player becomes detached from compositor timeline. TEST_F(LayerOwnerTestWithCompositor, DetachTimelineOnAnimatorDeletion) { - // This test is meaningless if CC timelines disabled. - if (!Layer::UILayerSettings().use_compositor_animation_timelines) - return; - scoped_ptr<Layer> root_layer(new Layer); compositor()->SetRootLayer(root_layer.get()); @@ -255,10 +289,6 @@ TEST_F(LayerOwnerTestWithCompositor, DetachTimelineOnAnimatorDeletion) { // then LayerAnimator's player becomes attached to timeline. TEST_F(LayerOwnerTestWithCompositor, AttachTimelineIfAnimatorCreatedAfterSetCompositor) { - // This test is meaningless if CC timelines disabled. - if (!Layer::UILayerSettings().use_compositor_animation_timelines) - return; - scoped_ptr<Layer> root_layer(new Layer); compositor()->SetRootLayer(root_layer.get()); diff --git a/chromium/ui/compositor/layer_threaded_animation_delegate.h b/chromium/ui/compositor/layer_threaded_animation_delegate.h new file mode 100644 index 00000000000..b921077e443 --- /dev/null +++ b/chromium/ui/compositor/layer_threaded_animation_delegate.h @@ -0,0 +1,26 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_COMPOSITOR_LAYER_THREADED_ANIMATION_DELEGATE_H_ +#define UI_COMPOSITOR_LAYER_THREADED_ANIMATION_DELEGATE_H_ + +#include "base/memory/scoped_ptr.h" +#include "cc/animation/animation.h" +#include "ui/compositor/compositor_export.h" + +namespace ui { + +// Attach CC animations using this interface. +class COMPOSITOR_EXPORT LayerThreadedAnimationDelegate { + public: + virtual void AddThreadedAnimation(scoped_ptr<cc::Animation> animation) = 0; + virtual void RemoveThreadedAnimation(int animation_id) = 0; + + protected: + virtual ~LayerThreadedAnimationDelegate() {} +}; + +} // namespace ui + +#endif // UI_COMPOSITOR_LAYER_THREADED_ANIMATION_DELEGATE_H_ diff --git a/chromium/ui/compositor/layer_unittest.cc b/chromium/ui/compositor/layer_unittest.cc index 70581b6d08e..c62177833f2 100644 --- a/chromium/ui/compositor/layer_unittest.cc +++ b/chromium/ui/compositor/layer_unittest.cc @@ -20,16 +20,17 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" -#include "cc/layers/delegated_frame_provider.h" -#include "cc/layers/delegated_frame_resource_collection.h" #include "cc/layers/layer.h" #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" -#include "cc/output/delegated_frame_data.h" +#include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_sequence.h" #include "cc/test/pixel_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/compositor_observer.h" #include "ui/compositor/dip_util.h" +#include "ui/compositor/layer_animation_element.h" +#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/layer_animator.h" #include "ui/compositor/paint_context.h" @@ -509,6 +510,23 @@ class LayerWithDelegateTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(LayerWithDelegateTest); }; +void ReturnMailbox(bool* run, const gpu::SyncToken& sync_token, bool is_lost) { + *run = true; +} + +TEST(LayerStandaloneTest, ReleaseMailboxOnDestruction) { + scoped_ptr<Layer> layer(new Layer(LAYER_TEXTURED)); + bool callback_run = false; + cc::TextureMailbox mailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), 0); + layer->SetTextureMailbox(mailbox, + cc::SingleReleaseCallback::Create( + base::Bind(ReturnMailbox, &callback_run)), + gfx::Size(10, 10)); + EXPECT_FALSE(callback_run); + layer.reset(); + EXPECT_TRUE(callback_run); +} + // L1 // +-- L2 TEST_F(LayerWithDelegateTest, ConvertPointToLayer_Simple) { @@ -696,8 +714,8 @@ TEST_F(LayerWithNullDelegateTest, EscapedDebugNames) { scoped_ptr<Layer> layer(CreateLayer(LAYER_NOT_DRAWN)); std::string name = "\"\'\\/\b\f\n\r\t\n"; layer->set_name(name); - scoped_refptr<base::trace_event::ConvertableToTraceFormat> debug_info = - layer->TakeDebugInfo(layer->cc_layer_for_testing()); + scoped_ptr<base::trace_event::ConvertableToTraceFormat> debug_info( + layer->TakeDebugInfo(layer->cc_layer_for_testing())); EXPECT_TRUE(debug_info.get()); std::string json; debug_info->AppendAsTraceFormat(&json); @@ -712,10 +730,6 @@ TEST_F(LayerWithNullDelegateTest, EscapedDebugNames) { EXPECT_EQ(name, roundtrip); } -void ReturnMailbox(bool* run, const gpu::SyncToken& sync_token, bool is_lost) { - *run = true; -} - TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { scoped_ptr<Layer> l1(CreateLayer(LAYER_SOLID_COLOR)); l1->SetFillsBoundsOpaquely(true); @@ -1472,68 +1486,13 @@ TEST_F(LayerWithDelegateTest, SetBoundsWhenInvisible) { EXPECT_TRUE(delegate.painted()); } -static scoped_ptr<cc::DelegatedFrameData> MakeFrameData(gfx::Size size) { - scoped_ptr<cc::DelegatedFrameData> frame_data(new cc::DelegatedFrameData); - scoped_ptr<cc::RenderPass> render_pass(cc::RenderPass::Create()); - render_pass->SetNew( - cc::RenderPassId(1, 1), gfx::Rect(size), gfx::Rect(), gfx::Transform()); - frame_data->render_pass_list.push_back(std::move(render_pass)); - return frame_data; -} - -TEST_F(LayerWithDelegateTest, DelegatedLayer) { - scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000))); - - scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED)); - - child->SetBounds(gfx::Rect(0, 0, 10, 10)); - child->SetVisible(true); - root->Add(child.get()); - DrawTree(root.get()); - - scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection = - new cc::DelegatedFrameResourceCollection; - scoped_refptr<cc::DelegatedFrameProvider> frame_provider; +namespace { - // Content matches layer size. - frame_provider = new cc::DelegatedFrameProvider( - resource_collection.get(), MakeFrameData(gfx::Size(10, 10))); - child->SetShowDelegatedContent(frame_provider.get(), gfx::Size(10, 10)); - EXPECT_EQ(child->cc_layer_for_testing()->bounds().ToString(), - gfx::Size(10, 10).ToString()); +void FakeSatisfyCallback(cc::SurfaceSequence) {} - // Content larger than layer. - child->SetBounds(gfx::Rect(0, 0, 5, 5)); - EXPECT_EQ(child->cc_layer_for_testing()->bounds().ToString(), - gfx::Size(5, 5).ToString()); +void FakeRequireCallback(cc::SurfaceId, cc::SurfaceSequence) {} - // Content smaller than layer. - child->SetBounds(gfx::Rect(0, 0, 10, 10)); - frame_provider = new cc::DelegatedFrameProvider( - resource_collection.get(), MakeFrameData(gfx::Size(5, 5))); - child->SetShowDelegatedContent(frame_provider.get(), gfx::Size(5, 5)); - EXPECT_EQ(child->cc_layer_for_testing()->bounds().ToString(), - gfx::Size(5, 5).ToString()); - - // Hi-DPI content on low-DPI layer. - frame_provider = new cc::DelegatedFrameProvider( - resource_collection.get(), MakeFrameData(gfx::Size(20, 20))); - child->SetShowDelegatedContent(frame_provider.get(), gfx::Size(10, 10)); - EXPECT_EQ(child->cc_layer_for_testing()->bounds().ToString(), - gfx::Size(10, 10).ToString()); - - // Hi-DPI content on hi-DPI layer. - compositor()->SetScaleAndSize(2.f, gfx::Size(1000, 1000)); - EXPECT_EQ(child->cc_layer_for_testing()->bounds().ToString(), - gfx::Size(10, 10).ToString()); - - // Low-DPI content on hi-DPI layer. - frame_provider = new cc::DelegatedFrameProvider( - resource_collection.get(), MakeFrameData(gfx::Size(10, 10))); - child->SetShowDelegatedContent(frame_provider.get(), gfx::Size(10, 10)); - EXPECT_EQ(child->cc_layer_for_testing()->bounds().ToString(), - gfx::Size(10, 10).ToString()); -} +} // namespace TEST_F(LayerWithDelegateTest, ExternalContent) { scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000))); @@ -1550,15 +1509,11 @@ TEST_F(LayerWithDelegateTest, ExternalContent) { EXPECT_TRUE(child->cc_layer_for_testing()); EXPECT_EQ(before.get(), child->cc_layer_for_testing()); - scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection = - new cc::DelegatedFrameResourceCollection; - scoped_refptr<cc::DelegatedFrameProvider> frame_provider = - new cc::DelegatedFrameProvider(resource_collection.get(), - MakeFrameData(gfx::Size(10, 10))); - - // Showing delegated content changes the underlying cc layer. + // Showing surface content changes the underlying cc layer. before = child->cc_layer_for_testing(); - child->SetShowDelegatedContent(frame_provider.get(), gfx::Size(10, 10)); + child->SetShowSurface(cc::SurfaceId(), base::Bind(&FakeSatisfyCallback), + base::Bind(&FakeRequireCallback), gfx::Size(10, 10), + 1.0, gfx::Size(10, 10)); EXPECT_TRUE(child->cc_layer_for_testing()); EXPECT_NE(before.get(), child->cc_layer_for_testing()); @@ -1581,15 +1536,11 @@ TEST_F(LayerWithDelegateTest, LayerFiltersSurvival) { EXPECT_EQ(layer->layer_grayscale(), 0.5f); EXPECT_EQ(1u, layer->cc_layer_for_testing()->filters().size()); - scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection = - new cc::DelegatedFrameResourceCollection; - scoped_refptr<cc::DelegatedFrameProvider> frame_provider = - new cc::DelegatedFrameProvider(resource_collection.get(), - MakeFrameData(gfx::Size(10, 10))); - - // Showing delegated content changes the underlying cc layer. + // Showing surface content changes the underlying cc layer. scoped_refptr<cc::Layer> before = layer->cc_layer_for_testing(); - layer->SetShowDelegatedContent(frame_provider.get(), gfx::Size(10, 10)); + layer->SetShowSurface(cc::SurfaceId(), base::Bind(&FakeSatisfyCallback), + base::Bind(&FakeRequireCallback), gfx::Size(10, 10), + 1.0, gfx::Size(10, 10)); EXPECT_EQ(layer->layer_grayscale(), 0.5f); EXPECT_TRUE(layer->cc_layer_for_testing()); EXPECT_NE(before.get(), layer->cc_layer_for_testing()); @@ -1605,41 +1556,41 @@ TEST_F(LayerWithRealCompositorTest, AddRemoveThreadedAnimations) { l1->SetAnimator(LayerAnimator::CreateImplicitAnimator()); l2->SetAnimator(LayerAnimator::CreateImplicitAnimator()); - EXPECT_FALSE(l1->HasPendingThreadedAnimations()); + EXPECT_FALSE(l1->HasPendingThreadedAnimationsForTesting()); // Trigger a threaded animation. l1->SetOpacity(0.5f); - EXPECT_TRUE(l1->HasPendingThreadedAnimations()); + EXPECT_TRUE(l1->HasPendingThreadedAnimationsForTesting()); // Ensure we can remove a pending threaded animation. l1->GetAnimator()->StopAnimating(); - EXPECT_FALSE(l1->HasPendingThreadedAnimations()); + EXPECT_FALSE(l1->HasPendingThreadedAnimationsForTesting()); // Trigger another threaded animation. l1->SetOpacity(0.2f); - EXPECT_TRUE(l1->HasPendingThreadedAnimations()); + EXPECT_TRUE(l1->HasPendingThreadedAnimationsForTesting()); root->Add(l1.get()); GetCompositor()->SetRootLayer(root.get()); // Now that l1 is part of a tree, it should have dispatched the pending // animation. - EXPECT_FALSE(l1->HasPendingThreadedAnimations()); + EXPECT_FALSE(l1->HasPendingThreadedAnimationsForTesting()); // Ensure that l1 no longer holds on to animations. l1->SetOpacity(0.1f); - EXPECT_FALSE(l1->HasPendingThreadedAnimations()); + EXPECT_FALSE(l1->HasPendingThreadedAnimationsForTesting()); // Ensure that adding a layer to an existing tree causes its pending // animations to get dispatched. l2->SetOpacity(0.5f); - EXPECT_TRUE(l2->HasPendingThreadedAnimations()); + EXPECT_TRUE(l2->HasPendingThreadedAnimationsForTesting()); l1->Add(l2.get()); - EXPECT_FALSE(l2->HasPendingThreadedAnimations()); + EXPECT_FALSE(l2->HasPendingThreadedAnimationsForTesting()); } // Tests that in-progress threaded animations complete when a Layer's @@ -1785,6 +1736,59 @@ TEST_F(LayerWithDelegateTest, DestroyingLayerRemovesTheAnimatorFromCollection) { EXPECT_FALSE(compositor()->layer_animator_collection()->HasActiveAnimators()); } +// A LayerAnimationObserver that removes a child layer from a parent when an +// animation completes. +class LayerRemovingLayerAnimationObserver : public LayerAnimationObserver { + public: + LayerRemovingLayerAnimationObserver(Layer* root, Layer* child) + : root_(root), child_(child) {} + + // LayerAnimationObserver: + void OnLayerAnimationEnded(LayerAnimationSequence* sequence) override { + root_->Remove(child_); + } + + void OnLayerAnimationAborted(LayerAnimationSequence* sequence) override { + root_->Remove(child_); + } + + void OnLayerAnimationScheduled(LayerAnimationSequence* sequence) override {} + + private: + Layer* root_; + Layer* child_; + + DISALLOW_COPY_AND_ASSIGN(LayerRemovingLayerAnimationObserver); +}; + +// Verifies that empty LayerAnimators are not left behind when removing child +// Layers that own an empty LayerAnimator. See http://crbug.com/552037. +TEST_F(LayerWithDelegateTest, NonAnimatingAnimatorsAreRemovedFromCollection) { + scoped_ptr<Layer> root(CreateLayer(LAYER_TEXTURED)); + scoped_ptr<Layer> parent(CreateLayer(LAYER_TEXTURED)); + scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED)); + root->Add(parent.get()); + parent->Add(child.get()); + compositor()->SetRootLayer(root.get()); + + child->SetAnimator(LayerAnimator::CreateDefaultAnimator()); + + LayerRemovingLayerAnimationObserver observer(root.get(), parent.get()); + child->GetAnimator()->AddObserver(&observer); + + LayerAnimationElement* element = + ui::LayerAnimationElement::CreateOpacityElement( + 0.5f, base::TimeDelta::FromSeconds(1)); + LayerAnimationSequence* sequence = new LayerAnimationSequence(element); + + child->GetAnimator()->StartAnimation(sequence); + EXPECT_TRUE(compositor()->layer_animator_collection()->HasActiveAnimators()); + + child->GetAnimator()->StopAnimating(); + EXPECT_FALSE(root->Contains(parent.get())); + EXPECT_FALSE(compositor()->layer_animator_collection()->HasActiveAnimators()); +} + namespace { std::string Vector2dFTo100thPercisionString(const gfx::Vector2dF& vector) { @@ -1854,12 +1858,9 @@ TEST(LayerDelegateTest, DelegatedFrameDamage) { FrameDamageCheckingDelegate delegate; layer->set_delegate(&delegate); - scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection = - new cc::DelegatedFrameResourceCollection; - scoped_refptr<cc::DelegatedFrameProvider> frame_provider( - new cc::DelegatedFrameProvider(resource_collection.get(), - MakeFrameData(gfx::Size(10, 10)))); - layer->SetShowDelegatedContent(frame_provider.get(), gfx::Size(10, 10)); + layer->SetShowSurface(cc::SurfaceId(), base::Bind(&FakeSatisfyCallback), + base::Bind(&FakeRequireCallback), gfx::Size(10, 10), + 1.0, gfx::Size(10, 10)); EXPECT_FALSE(delegate.delegated_frame_damage_called()); layer->OnDelegatedFrameDamage(damage_rect); diff --git a/chromium/ui/compositor/paint_recorder.cc b/chromium/ui/compositor/paint_recorder.cc index fef53c8a196..163a7a77908 100644 --- a/chromium/ui/compositor/paint_recorder.cc +++ b/chromium/ui/compositor/paint_recorder.cc @@ -21,8 +21,7 @@ PaintRecorder::PaintRecorder(const PaintContext& context, // the recorder_ so no need to store a RefPtr to it on this class, we just // store the gfx::Canvas. canvas_(skia::SharePtr(context.recorder_->beginRecording( - gfx::RectToSkRect(gfx::Rect(recording_size)))) - .get(), + gfx::RectToSkRect(gfx::Rect(recording_size)))), context.device_scale_factor_), cache_(cache), bounds_in_layer_(context.ToLayerSpaceBounds(recording_size)) { @@ -44,7 +43,7 @@ PaintRecorder::~PaintRecorder() { const auto& item = context_.list_->CreateAndAppendItem<cc::DrawingDisplayItem>( bounds_in_layer_, - skia::AdoptRef(context_.recorder_->endRecordingAsPicture())); + context_.recorder_->finishRecordingAsPicture()); if (cache_) cache_->SetCache(item); } diff --git a/chromium/ui/compositor/transform_animation_curve_adapter.cc b/chromium/ui/compositor/transform_animation_curve_adapter.cc index 5439e0a1ac7..c3f2a31bf50 100644 --- a/chromium/ui/compositor/transform_animation_curve_adapter.cc +++ b/chromium/ui/compositor/transform_animation_curve_adapter.cc @@ -21,6 +21,9 @@ TransformAnimationCurveAdapter::TransformAnimationCurveAdapter( gfx::DecomposeTransform(&decomposed_target_value_, target_value_); } +TransformAnimationCurveAdapter::TransformAnimationCurveAdapter( + const TransformAnimationCurveAdapter& other) = default; + TransformAnimationCurveAdapter::~TransformAnimationCurveAdapter() { } diff --git a/chromium/ui/compositor/transform_animation_curve_adapter.h b/chromium/ui/compositor/transform_animation_curve_adapter.h index 05b5644292e..69332414793 100644 --- a/chromium/ui/compositor/transform_animation_curve_adapter.h +++ b/chromium/ui/compositor/transform_animation_curve_adapter.h @@ -23,6 +23,8 @@ class COMPOSITOR_EXPORT TransformAnimationCurveAdapter gfx::Transform target_value, base::TimeDelta duration); + TransformAnimationCurveAdapter(const TransformAnimationCurveAdapter& other); + ~TransformAnimationCurveAdapter() override; // TransformAnimationCurve implementation. diff --git a/chromium/ui/display/BUILD.gn b/chromium/ui/display/BUILD.gn index 6c6c181ae1e..226485b3c11 100644 --- a/chromium/ui/display/BUILD.gn +++ b/chromium/ui/display/BUILD.gn @@ -5,25 +5,79 @@ import("//build/config/ui.gni") import("//testing/test.gni") -if (is_chromeos) { - component("display") { - sources = [ - "chromeos/apply_content_protection_task.cc", - "chromeos/apply_content_protection_task.h", - "chromeos/configure_displays_task.cc", - "chromeos/configure_displays_task.h", - "chromeos/display_configurator.cc", - "chromeos/display_configurator.h", - "chromeos/display_layout_manager.h", - "chromeos/display_snapshot_virtual.cc", - "chromeos/display_snapshot_virtual.h", - "chromeos/display_util.cc", - "chromeos/display_util.h", - "chromeos/ozone/display_configurator_ozone.cc", - "chromeos/query_content_protection_task.cc", - "chromeos/query_content_protection_task.h", - "chromeos/update_display_configuration_task.cc", - "chromeos/update_display_configuration_task.h", +component("display") { + sources = [ + "chromeos/apply_content_protection_task.cc", + "chromeos/apply_content_protection_task.h", + "chromeos/configure_displays_task.cc", + "chromeos/configure_displays_task.h", + "chromeos/display_configurator.cc", + "chromeos/display_configurator.h", + "chromeos/display_layout_manager.h", + "chromeos/display_snapshot_virtual.cc", + "chromeos/display_snapshot_virtual.h", + "chromeos/display_util.cc", + "chromeos/display_util.h", + "chromeos/ozone/display_configurator_ozone.cc", + "chromeos/query_content_protection_task.cc", + "chromeos/query_content_protection_task.h", + "chromeos/update_display_configuration_task.cc", + "chromeos/update_display_configuration_task.h", + "chromeos/x11/display_configurator_x11.cc", + "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", + "display_export.h", + "display_switches.cc", + "display_switches.h", + "manager/display_layout.cc", + "manager/display_layout.h", + "manager/display_layout_builder.cc", + "manager/display_layout_builder.h", + "win/display_info.cc", + "win/display_info.h", + "win/screen_win.cc", + "win/screen_win.h", + "win/screen_win_display.cc", + "win/screen_win_display.h", + ] + + defines = [ "DISPLAY_IMPLEMENTATION" ] + + deps = [ + "//base", + "//ui/display/types", + "//ui/display/util", + "//ui/gfx", + "//ui/gfx/geometry", + ] + + if (is_chromeos && use_x11) { + sources -= [ "chromeos/ozone/display_configurator_ozone.cc" ] + configs += [ + "//build/config/linux:x11", + "//build/config/linux:xext", + "//build/config/linux:xi", + "//build/config/linux:xrandr", + ] + deps += [ "//ui/events/platform" ] + } + + deps += [] + if (use_x11) { + deps += [ "//ui/gfx/x" ] + } + + if (is_chromeos && use_ozone) { + deps += [ "//ui/ozone" ] + sources -= [ "chromeos/x11/display_configurator_x11.cc", "chromeos/x11/display_mode_x11.cc", "chromeos/x11/display_mode_x11.h", @@ -35,55 +89,11 @@ if (is_chromeos) { "chromeos/x11/native_display_delegate_x11.h", "chromeos/x11/native_display_event_dispatcher_x11.cc", "chromeos/x11/native_display_event_dispatcher_x11.h", - "display_export.h", - "display_switches.cc", - "display_switches.h", - ] - - defines = [ "DISPLAY_IMPLEMENTATION" ] - - deps = [ - "//base", - "//ui/display/types", - "//ui/display/util", - "//ui/gfx", - "//ui/gfx/geometry", ] - - if (use_x11) { - sources -= [ "chromeos/ozone/display_configurator_ozone.cc" ] - configs += [ - "//build/config/linux:x11", - "//build/config/linux:xext", - "//build/config/linux:xi", - "//build/config/linux:xrandr", - ] - deps += [ "//ui/events/platform" ] - } - - deps += [] - if (use_x11) { - deps += [ "//ui/gfx/x" ] - } - - if (use_ozone) { - deps += [ "//ui/ozone" ] - sources -= [ - "chromeos/x11/display_configurator_x11.cc", - "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", - ] - } } +} +if (is_chromeos) { component("test_util") { output_name = "display_test_util" sources = [ @@ -103,44 +113,34 @@ if (is_chromeos) { "//ui/gfx/geometry", ] } - - source_set("test_support") { - testonly = true - sources = [ - "chromeos/test/action_logger.cc", - "chromeos/test/action_logger.h", - "chromeos/test/action_logger_util.cc", - "chromeos/test/action_logger_util.h", - "chromeos/test/test_display_layout_manager.cc", - "chromeos/test/test_display_layout_manager.h", - "chromeos/test/test_native_display_delegate.cc", - "chromeos/test/test_native_display_delegate.h", - ] - - public_deps = [ - ":display", - ] - deps = [ - "//base", - "//ui/display/types", - "//ui/gfx", - "//ui/gfx:test_support", - "//ui/gfx/geometry", - ] - } } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("display_unittests_run") { +source_set("test_support") { testonly = true + sources = [ + "chromeos/test/action_logger.cc", + "chromeos/test/action_logger.h", + "chromeos/test/action_logger_util.cc", + "chromeos/test/action_logger_util.h", + "chromeos/test/test_display_layout_manager.cc", + "chromeos/test/test_display_layout_manager.h", + "chromeos/test/test_native_display_delegate.cc", + "chromeos/test/test_native_display_delegate.h", + ] + + public_deps = [ + ":display", + ] deps = [ - ":display_unittests", + "//base", + "//ui/display/types", + "//ui/gfx", + "//ui/gfx:test_support", + "//ui/gfx/geometry", ] } -# This test covers the ChromeOS "display" target as well as the cross-platform -# //display/util target. +# This test covers all testable components in display. test("display_unittests") { sources = [ "chromeos/apply_content_protection_task_unittest.cc", @@ -150,29 +150,32 @@ test("display_unittests") { "chromeos/update_display_configuration_task_unittest.cc", "chromeos/x11/display_util_x11_unittest.cc", "chromeos/x11/native_display_event_dispatcher_x11_unittest.cc", + "manager/display_layout_builder_unittest.cc", "util/display_util_unittest.cc", "util/edid_parser_unittest.cc", + "win/screen_win_unittest.cc", ] deps = [ + ":display", "//base", "//base/test:run_all_unittests", + "//base/test:test_support", "//testing/gtest", + "//ui/display/types", "//ui/display/util", + "//ui/gfx:test_support", "//ui/gfx/geometry", ] if (is_chromeos) { deps += [ - ":display", ":test_support", ":test_util", - "//base/test:test_support", - "//ui/display/types", ] } - if (use_ozone && is_chromeos) { + if (is_chromeos && use_ozone) { sources -= [ "chromeos/x11/display_util_x11_unittest.cc", "chromeos/x11/native_display_event_dispatcher_x11_unittest.cc", diff --git a/chromium/ui/display/DEPS b/chromium/ui/display/DEPS index 7d1062cee84..e3c5f64e455 100644 --- a/chromium/ui/display/DEPS +++ b/chromium/ui/display/DEPS @@ -1,5 +1,4 @@ include_rules = [ "+third_party/cros_system_api", - "+ui/gfx/display.h", - "+ui/gfx/geometry", + "+ui/gfx", ] diff --git a/chromium/ui/display/display.gyp b/chromium/ui/display/display.gyp index fe32ea3bbf7..a5c464489b8 100644 --- a/chromium/ui/display/display.gyp +++ b/chromium/ui/display/display.gyp @@ -76,6 +76,16 @@ 'display_export.h', 'display_switches.cc', 'display_switches.h', + 'manager/display_layout.cc', + 'manager/display_layout.h', + 'manager/display_layout_builder.cc', + 'manager/display_layout_builder.h', + 'win/display_info.cc', + 'win/display_info.h', + 'win/screen_win.cc', + 'win/screen_win.h', + 'win/screen_win_display.cc', + 'win/screen_win_display.h', ], 'conditions': [ ['use_x11 == 1', { @@ -196,6 +206,7 @@ '../../testing/gtest.gyp:gtest', '../../ui/gfx/gfx.gyp:gfx_geometry', '../../ui/gfx/gfx.gyp:gfx_test_support', + 'display', 'display_util', ], 'include_dirs': [ @@ -209,8 +220,10 @@ 'chromeos/update_display_configuration_task_unittest.cc', 'chromeos/x11/display_util_x11_unittest.cc', 'chromeos/x11/native_display_event_dispatcher_x11_unittest.cc', + 'manager/display_layout_builder_unittest.cc', 'util/display_util_unittest.cc', 'util/edid_parser_unittest.cc', + 'win/screen_win_unittest.cc', ], 'conditions': [ ['chromeos == 1', { diff --git a/chromium/ui/display/display_unittests.isolate b/chromium/ui/display/display_unittests.isolate index 8122341dcc1..a25fc639d45 100644 --- a/chromium/ui/display/display_unittests.isolate +++ b/chromium/ui/display/display_unittests.isolate @@ -3,7 +3,7 @@ # found in the LICENSE file. { 'conditions': [ - ['OS=="linux"', { + ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ '../../testing/test_env.py', diff --git a/chromium/ui/display/manager/display_layout.cc b/chromium/ui/display/manager/display_layout.cc new file mode 100644 index 00000000000..8812888530e --- /dev/null +++ b/chromium/ui/display/manager/display_layout.cc @@ -0,0 +1,256 @@ +// 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/display/manager/display_layout.h" + +#include <algorithm> +#include <sstream> + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/values.h" +#include "ui/gfx/display.h" + +namespace display { +namespace { + +// DisplayPlacement Positions +const char kTop[] = "top"; +const char kRight[] = "right"; +const char kBottom[] = "bottom"; +const char kLeft[] = "left"; +const char kUnknown[] = "unknown"; + +// The maximum value for 'offset' in DisplayLayout in case of outliers. Need +// to change this value in case to support even larger displays. +const int kMaxValidOffset = 10000; + +bool IsIdInList(int64_t id, const DisplayIdList& list) { + const auto iter = + std::find_if(list.begin(), list.end(), + [id](int64_t display_id) { return display_id == id; }); + return iter != list.end(); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// DisplayPlacement + +DisplayPlacement::DisplayPlacement() + : display_id(gfx::Display::kInvalidDisplayID), + parent_display_id(gfx::Display::kInvalidDisplayID), + position(DisplayPlacement::RIGHT), + offset(0) {} + +DisplayPlacement::DisplayPlacement(Position pos, int offset) + : display_id(gfx::Display::kInvalidDisplayID), + parent_display_id(gfx::Display::kInvalidDisplayID), + position(pos), + offset(offset) { + DCHECK_LE(TOP, position); + DCHECK_GE(LEFT, position); + // Set the default value to |position| in case position is invalid. DCHECKs + // above doesn't stop in Release builds. + if (TOP > position || LEFT < position) + this->position = RIGHT; + + DCHECK_GE(kMaxValidOffset, abs(offset)); +} + +DisplayPlacement::DisplayPlacement(const DisplayPlacement& placement) + : display_id(placement.display_id), + parent_display_id(placement.parent_display_id), + position(placement.position), + offset(placement.offset) {} + +DisplayPlacement& DisplayPlacement::Swap() { + switch (position) { + case TOP: + position = BOTTOM; + break; + case BOTTOM: + position = TOP; + break; + case RIGHT: + position = LEFT; + break; + case LEFT: + position = RIGHT; + break; + } + offset = -offset; + std::swap(display_id, parent_display_id); + return *this; +} + +std::string DisplayPlacement::ToString() const { + std::stringstream s; + if (display_id != gfx::Display::kInvalidDisplayID) + s << "id=" << display_id << ", "; + if (parent_display_id != gfx::Display::kInvalidDisplayID) + s << "parent=" << parent_display_id << ", "; + s << PositionToString(position) << ", "; + s << offset; + return s.str(); +} + +// static +std::string DisplayPlacement::PositionToString(Position position) { + switch (position) { + case TOP: + return kTop; + case RIGHT: + return kRight; + case BOTTOM: + return kBottom; + case LEFT: + return kLeft; + } + return kUnknown; +} + +// static +bool DisplayPlacement::StringToPosition(const base::StringPiece& string, + Position* position) { + if (string == kTop) { + *position = TOP; + return true; + } + + if (string == kRight) { + *position = RIGHT; + return true; + } + + if (string == kBottom) { + *position = BOTTOM; + return true; + } + + if (string == kLeft) { + *position = LEFT; + return true; + } + + LOG(ERROR) << "Invalid position value:" << string; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// DisplayLayout + +DisplayLayout::DisplayLayout() + : mirrored(false), + default_unified(true), + primary_id(gfx::Display::kInvalidDisplayID) {} + +DisplayLayout::~DisplayLayout() {} + +// static +bool DisplayLayout::Validate(const DisplayIdList& list, + const DisplayLayout& layout) { + // The primary display should be in the list. + DCHECK(IsIdInList(layout.primary_id, list)); + + // Unified mode, or mirror mode switched from unified mode, + // may not have the placement yet. + if (layout.placement_list.size() == 0u) + return true; + + bool has_primary_as_parent = false; + int64_t id = 0; + + for (const auto& placement : layout.placement_list) { + // Placements are sorted by display_id. + if (id >= placement.display_id) { + LOG(ERROR) << "PlacementList must be sorted by display_id"; + return false; + } + if (placement.display_id == gfx::Display::kInvalidDisplayID) { + LOG(ERROR) << "display_id is not initialized"; + return false; + } + if (placement.parent_display_id == gfx::Display::kInvalidDisplayID) { + LOG(ERROR) << "display_parent_id is not initialized"; + return false; + } + if (placement.display_id == placement.parent_display_id) { + LOG(ERROR) << "display_id must not be same as parent_display_id"; + return false; + } + if (!IsIdInList(placement.display_id, list)) { + LOG(ERROR) << "display_id is not in the id list:" << placement.ToString(); + return false; + } + + if (!IsIdInList(placement.parent_display_id, list)) { + LOG(ERROR) << "parent_display_id is not in the id list:" + << placement.ToString(); + return false; + } + has_primary_as_parent |= layout.primary_id == placement.parent_display_id; + } + if (!has_primary_as_parent) + LOG(ERROR) << "At least, one placement must have the primary as a parent."; + return has_primary_as_parent; +} + +scoped_ptr<DisplayLayout> DisplayLayout::Copy() const { + scoped_ptr<DisplayLayout> copy(new DisplayLayout); + for (const auto& placement : placement_list) + copy->placement_list.push_back(placement); + copy->mirrored = mirrored; + copy->default_unified = default_unified; + copy->primary_id = primary_id; + return copy; +} + +bool DisplayLayout::HasSamePlacementList(const DisplayLayout& layout) const { + if (placement_list.size() != layout.placement_list.size()) + return false; + for (size_t index = 0; index < placement_list.size(); index++) { + const DisplayPlacement& placement1 = placement_list[index]; + const DisplayPlacement& placement2 = layout.placement_list[index]; + if (placement1.position != placement2.position || + placement1.offset != placement2.offset || + placement1.display_id != placement2.display_id || + placement1.parent_display_id != placement2.parent_display_id) { + return false; + } + } + return true; +} + +std::string DisplayLayout::ToString() const { + std::stringstream s; + s << "primary=" << primary_id; + if (mirrored) + s << ", mirrored"; + if (default_unified) + s << ", default_unified"; + bool added = false; + for (const auto& placement : placement_list) { + s << (added ? "),(" : " [("); + s << placement.ToString(); + added = true; + } + if (added) + s << ")]"; + return s.str(); +} + +DisplayPlacement DisplayLayout::FindPlacementById(int64_t display_id) const { + const auto iter = + std::find_if(placement_list.begin(), placement_list.end(), + [display_id](const DisplayPlacement& placement) { + return placement.display_id == display_id; + }); + return (iter == placement_list.end()) ? DisplayPlacement() + : DisplayPlacement(*iter); +} + +} // namespace display diff --git a/chromium/ui/display/manager/display_layout.h b/chromium/ui/display/manager/display_layout.h new file mode 100644 index 00000000000..7f5c99c8282 --- /dev/null +++ b/chromium/ui/display/manager/display_layout.h @@ -0,0 +1,114 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_DISPLAY_MANAGER_DISPLAY_LAYOUT_H_ +#define UI_DISPLAY_MANAGER_DISPLAY_LAYOUT_H_ + +#include <stdint.h> + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "ui/display/display_export.h" + +namespace base { +class Value; +template <typename T> class JSONValueConverter; +} + +namespace gfx { +class Display; +} + +namespace display { + +// An identifier used to manage display layout in DisplayManager / +// DisplayLayoutStore. +using DisplayIdList = std::vector<int64_t>; + +using DisplayList = std::vector<gfx::Display>; + +// DisplayPlacement specifies where the display (D) is placed relative +// to parent (P) display. In the following example, the display (D) +// given by |display_id| is placed at the left side of the parent +// display (P) given by |parent_display_id|, with a negative offset. +// +// + +--------+ +// offset | | | +// + | D +--------+ +// | | | +// +--------+ P | +// | | +// +--------+ +// +struct DISPLAY_EXPORT DisplayPlacement { + // The id of the display this placement will be applied to. + int64_t display_id; + + // The parent display id to which the above display is placed. + int64_t parent_display_id; + + // To which side the parent display the display is positioned. + enum Position { TOP, RIGHT, BOTTOM, LEFT }; + Position position; + + // The offset of the position of the secondary display. The offset is + // based on the top/left edge of the primary display. + int offset; + + DisplayPlacement(Position position, int offset); + DisplayPlacement(); + DisplayPlacement(const DisplayPlacement& placement); + + DisplayPlacement& Swap(); + + std::string ToString() const; + + static std::string PositionToString(Position position); + static bool StringToPosition(const base::StringPiece& string, + Position* position); +}; + +class DISPLAY_EXPORT DisplayLayout final { + public: + DisplayLayout(); + ~DisplayLayout(); + + // Validates the layout object. + static bool Validate(const DisplayIdList& list, const DisplayLayout& layout); + + std::vector<DisplayPlacement> placement_list; + + // True if displays are mirrored. + bool mirrored; + + // True if multi displays should default to unified mode. + bool default_unified; + + // The id of the display used as a primary display. + int64_t primary_id; + + scoped_ptr<DisplayLayout> Copy() const; + + // Test if the |layout| has the same placement list. Other fields such + // as mirrored, primary_id are ignored. + bool HasSamePlacementList(const DisplayLayout& layout) const; + + // Returns string representation of the layout for debugging/testing. + std::string ToString() const; + + // Returns the DisplayPlacement entry matching |display_id| if it exists, + // otherwise returns a DisplayPlacement with an invalid display id. + DisplayPlacement FindPlacementById(int64_t display_id) const; + + private: + DISALLOW_COPY_AND_ASSIGN(DisplayLayout); +}; + +} // namespace display + +#endif // UI_DISPLAY_MANAGER_DISPLAY_LAYOUT_H_ diff --git a/chromium/ui/display/manager/display_layout_builder.cc b/chromium/ui/display/manager/display_layout_builder.cc new file mode 100644 index 00000000000..8376e74313d --- /dev/null +++ b/chromium/ui/display/manager/display_layout_builder.cc @@ -0,0 +1,68 @@ +// 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/display/manager/display_layout_builder.h" + +#include <algorithm> + +namespace display { + +DisplayLayoutBuilder::DisplayLayoutBuilder(const DisplayLayout& layout) + : layout_(layout.Copy()) {} + +DisplayLayoutBuilder::DisplayLayoutBuilder(int64_t primary_id) + : layout_(new DisplayLayout) { + layout_->primary_id = primary_id; +} + +DisplayLayoutBuilder::~DisplayLayoutBuilder() {} + +DisplayLayoutBuilder& DisplayLayoutBuilder::SetDefaultUnified( + bool default_unified) { + layout_->default_unified = default_unified; + return *this; +} + +DisplayLayoutBuilder& DisplayLayoutBuilder::SetMirrored(bool mirrored) { + layout_->mirrored = mirrored; + return *this; +} + +DisplayLayoutBuilder& DisplayLayoutBuilder::ClearPlacements() { + layout_->placement_list.clear(); + return *this; +} + +DisplayLayoutBuilder& DisplayLayoutBuilder::AddDisplayPlacement( + int64_t display_id, + int64_t parent_display_id, + DisplayPlacement::Position position, + int offset) { + DisplayPlacement placement; + placement.position = position; + placement.offset = offset; + placement.display_id = display_id; + placement.parent_display_id = parent_display_id; + layout_->placement_list.push_back(placement); + return *this; +} + +DisplayLayoutBuilder& DisplayLayoutBuilder::SetSecondaryPlacement( + int64_t secondary_id, + DisplayPlacement::Position position, + int offset) { + layout_->placement_list.clear(); + AddDisplayPlacement(secondary_id, layout_->primary_id, position, offset); + return *this; +} + +scoped_ptr<DisplayLayout> DisplayLayoutBuilder::Build() { + std::sort(layout_->placement_list.begin(), layout_->placement_list.end(), + [](const DisplayPlacement& a, const DisplayPlacement& b) { + return a.display_id < b.display_id; + }); + return std::move(layout_); +} + +} // namespace display diff --git a/chromium/ui/display/manager/display_layout_builder.h b/chromium/ui/display/manager/display_layout_builder.h new file mode 100644 index 00000000000..9b3caefac62 --- /dev/null +++ b/chromium/ui/display/manager/display_layout_builder.h @@ -0,0 +1,57 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_DISPLAY_MANAGER_DISPLAY_LAYOUT_BUILDER_H_ +#define UI_DISPLAY_MANAGER_DISPLAY_LAYOUT_BUILDER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/display/display_export.h" +#include "ui/display/manager/display_layout.h" + +namespace display { + +class DisplayLayout; + +// A utility class to create a DisplayLayout instance. +class DISPLAY_EXPORT DisplayLayoutBuilder final { + public: + // Creates a builder that uses a copy of the |layout| as a source. + explicit DisplayLayoutBuilder(const DisplayLayout& layout); + + // Creates a builder with the primary display id. + explicit DisplayLayoutBuilder(int64_t primary_id); + + ~DisplayLayoutBuilder(); + + DisplayLayoutBuilder& SetDefaultUnified(bool default_unified); + + DisplayLayoutBuilder& SetMirrored(bool mirrored); + + DisplayLayoutBuilder& ClearPlacements(); + + // Adds a display placement. + DisplayLayoutBuilder& AddDisplayPlacement(int64_t display_id, + int64_t parent_id, + DisplayPlacement::Position position, + int offset); + + // Sets the display placement for the secondary display. + DisplayLayoutBuilder& SetSecondaryPlacement( + int64_t secondary_id, + DisplayPlacement::Position position, + int offset); + + // Returns the DisplayLayout. After this call, the builder becomes invalid. + scoped_ptr<DisplayLayout> Build(); + + private: + scoped_ptr<DisplayLayout> layout_; + + DISALLOW_COPY_AND_ASSIGN(DisplayLayoutBuilder); +}; + +} // namespace display + +#endif // UI_DISPLAY_MANAGER_DISPLAY_LAYOUT_BUILDER_H_ diff --git a/chromium/ui/display/manager/display_layout_builder_unittest.cc b/chromium/ui/display/manager/display_layout_builder_unittest.cc new file mode 100644 index 00000000000..591a16cbd29 --- /dev/null +++ b/chromium/ui/display/manager/display_layout_builder_unittest.cc @@ -0,0 +1,49 @@ +// 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/display/manager/display_layout_builder.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace display { + +TEST(DisplayLayoutBuilderTest, SecondaryPlacement) { + DisplayLayoutBuilder builder(1); + builder.SetSecondaryPlacement(2, DisplayPlacement::LEFT, 30); + scoped_ptr<DisplayLayout> layout(builder.Build()); + ASSERT_EQ(1u, layout->placement_list.size()); + + EXPECT_EQ(2, layout->placement_list[0].display_id); + EXPECT_EQ(1, layout->placement_list[0].parent_display_id); + EXPECT_EQ(30, layout->placement_list[0].offset); + EXPECT_EQ(DisplayPlacement::LEFT, layout->placement_list[0].position); +} + +TEST(DisplayLayoutBuilderTest, MultiplePlacement) { + DisplayLayoutBuilder builder(1); + builder.AddDisplayPlacement(5, 1, DisplayPlacement::TOP, 30); + builder.AddDisplayPlacement(3, 5, DisplayPlacement::LEFT, 20); + builder.AddDisplayPlacement(4, 5, DisplayPlacement::RIGHT, 10); + scoped_ptr<DisplayLayout> layout(builder.Build()); + + ASSERT_EQ(3u, layout->placement_list.size()); + + // placmenets are sorted by display_id. + EXPECT_EQ(3, layout->placement_list[0].display_id); + EXPECT_EQ(5, layout->placement_list[0].parent_display_id); + EXPECT_EQ(20, layout->placement_list[0].offset); + EXPECT_EQ(DisplayPlacement::LEFT, layout->placement_list[0].position); + + EXPECT_EQ(4, layout->placement_list[1].display_id); + EXPECT_EQ(5, layout->placement_list[1].parent_display_id); + EXPECT_EQ(10, layout->placement_list[1].offset); + EXPECT_EQ(DisplayPlacement::RIGHT, layout->placement_list[1].position); + + EXPECT_EQ(5, layout->placement_list[2].display_id); + EXPECT_EQ(1, layout->placement_list[2].parent_display_id); + EXPECT_EQ(30, layout->placement_list[2].offset); + EXPECT_EQ(DisplayPlacement::TOP, layout->placement_list[2].position); +} + +} // namespace display diff --git a/chromium/ui/display/types/display_snapshot.cc b/chromium/ui/display/types/display_snapshot.cc index 30e3ea890b2..c3f6e382f74 100644 --- a/chromium/ui/display/types/display_snapshot.cc +++ b/chromium/ui/display/types/display_snapshot.cc @@ -4,17 +4,31 @@ #include "ui/display/types/display_snapshot.h" +#include <algorithm> + namespace ui { +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; + +} // namespace + DisplaySnapshot::DisplaySnapshot(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, const std::vector<const DisplayMode*>& modes, + const std::vector<uint8_t>& edid, const DisplayMode* current_mode, const DisplayMode* native_mode) : display_id_(display_id), @@ -23,12 +37,21 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id, type_(type), is_aspect_preserving_scaling_(is_aspect_preserving_scaling), has_overscan_(has_overscan), + has_color_correction_matrix_(has_color_correction_matrix), display_name_(display_name), sys_path_(sys_path), modes_(modes), + edid_(edid), current_mode_(current_mode), native_mode_(native_mode), - product_id_(kInvalidProductID) {} + product_id_(kInvalidProductID) { + // We must explicitly clear out the bytes that represent the serial number. + const size_t end = + std::min(kSerialNumberBeginingByte + kSerialNumberLengthInBytes, + edid_.size()); + for (size_t i = kSerialNumberBeginingByte; i < end; ++i) + edid_[i] = 0; +} DisplaySnapshot::~DisplaySnapshot() {} diff --git a/chromium/ui/display/types/display_snapshot.h b/chromium/ui/display/types/display_snapshot.h index 7f697984fe5..d7106916bc0 100644 --- a/chromium/ui/display/types/display_snapshot.h +++ b/chromium/ui/display/types/display_snapshot.h @@ -29,9 +29,11 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { DisplayConnectionType type, bool is_aspect_preserving_scaling, bool has_overscan, + bool has_color_correction_matrix, std::string display_name, const base::FilePath& sys_path, const std::vector<const DisplayMode*>& modes, + const std::vector<uint8_t>& edid, const DisplayMode* current_mode, const DisplayMode* native_mode); virtual ~DisplaySnapshot(); @@ -53,11 +55,17 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { int64_t product_id() const { return product_id_; } const std::vector<const DisplayMode*>& modes() const { return modes_; } + const std::vector<uint8_t>& edid() const { return edid_; } void set_current_mode(const DisplayMode* mode) { current_mode_ = mode; } void set_origin(const gfx::Point& origin) { origin_ = origin; } void add_mode(const DisplayMode* mode) { modes_.push_back(mode); } + // Whether this display has advanced color correction available. + bool has_color_correction_matrix() const { + return has_color_correction_matrix_; + } + // Returns a textual representation of this display state. virtual std::string ToString() const = 0; @@ -79,12 +87,18 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { bool has_overscan_; + bool has_color_correction_matrix_; + std::string display_name_; base::FilePath sys_path_; std::vector<const DisplayMode*> modes_; // Not owned. + // The display's EDID. It can be empty if nothing extracted such as in the + // case of a virtual display. + std::vector<uint8_t> edid_; + // Mode currently being used by the output. const DisplayMode* current_mode_; diff --git a/chromium/ui/display/types/native_display_delegate.h b/chromium/ui/display/types/native_display_delegate.h index bf59dca8ff0..b17be8f1cff 100644 --- a/chromium/ui/display/types/native_display_delegate.h +++ b/chromium/ui/display/types/native_display_delegate.h @@ -107,9 +107,12 @@ class DISPLAY_TYPES_EXPORT NativeDisplayDelegate { const ui::DisplaySnapshot& output, ui::ColorCalibrationProfile new_profile) = 0; - // Set the gamma ramp for the display. - virtual bool SetGammaRamp(const ui::DisplaySnapshot& output, - const std::vector<GammaRampRGBEntry>& lut) = 0; + // Set the gamma tables and corection matrix for the display. + virtual bool SetColorCorrection( + const ui::DisplaySnapshot& output, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) = 0; virtual void AddObserver(NativeDisplayObserver* observer) = 0; diff --git a/chromium/ui/display/util/x11/edid_parser_x11.cc b/chromium/ui/display/util/x11/edid_parser_x11.cc index 13059f0fea1..01b6f00e94b 100644 --- a/chromium/ui/display/util/x11/edid_parser_x11.cc +++ b/chromium/ui/display/util/x11/edid_parser_x11.cc @@ -75,48 +75,35 @@ bool GetEDIDProperty(XID output, std::vector<uint8_t>* edid) { return true; } -// Gets some useful data from the specified output device, such like -// manufacturer's ID, product code, and human readable name. Returns false if it -// fails to get those data and doesn't touch manufacturer ID/product code/name. -// nullptr can be passed for unwanted output parameters. -bool GetOutputDeviceData(XID output, - uint16_t* manufacturer_id, - std::string* human_readable_name) { - std::vector<uint8_t> edid; - if (!GetEDIDProperty(output, &edid)) - return false; +} // namespace - return ParseOutputDeviceData(edid, manufacturer_id, nullptr, - human_readable_name, nullptr, nullptr); +EDIDParserX11::EDIDParserX11(XID output_id) + : output_id_(output_id) { + GetEDIDProperty(output_id_, &edid_); } -} // namespace +EDIDParserX11::~EDIDParserX11() { +} -bool GetDisplayId(XID output_id, - uint8_t output_index, - int64_t* display_id_out) { - std::vector<uint8_t> edid; - if (!GetEDIDProperty(output_id, &edid)) +bool EDIDParserX11::GetDisplayId(uint8_t index, int64_t* out_display_id) const { + if (edid_.empty()) return false; - bool result = - GetDisplayIdFromEDID(edid, output_index, display_id_out, nullptr); - return result; + return GetDisplayIdFromEDID(edid_, index, out_display_id, nullptr); } -std::string GetDisplayName(RROutput output) { +std::string EDIDParserX11::GetDisplayName() const { std::string display_name; - GetOutputDeviceData(output, nullptr, &display_name); + ParseOutputDeviceData(edid_, nullptr, nullptr, &display_name, nullptr, + nullptr); return display_name; } -bool GetOutputOverscanFlag(RROutput output, bool* flag) { - std::vector<uint8_t> edid; - if (!GetEDIDProperty(output, &edid)) +bool EDIDParserX11::GetOutputOverscanFlag(bool* out_flag) const { + if (edid_.empty()) return false; - bool found = ParseOutputOverscanFlag(edid, flag); - return found; + return ParseOutputOverscanFlag(edid_, out_flag); } } // namespace ui diff --git a/chromium/ui/display/util/x11/edid_parser_x11.h b/chromium/ui/display/util/x11/edid_parser_x11.h index 55048e699e4..43f8cd1a48f 100644 --- a/chromium/ui/display/util/x11/edid_parser_x11.h +++ b/chromium/ui/display/util/x11/edid_parser_x11.h @@ -8,7 +8,9 @@ #include <stdint.h> #include <string> +#include <vector> +#include "base/macros.h" #include "ui/display/types/display_constants.h" #include "ui/display/util/display_util_export.h" @@ -19,21 +21,39 @@ typedef XID RROutput; namespace ui { -// Gets the EDID data from |output| and generates the display id through -// |GetDisplayIdFromEDID|. -DISPLAY_UTIL_EXPORT bool GetDisplayId(XID output, - uint8_t index, - int64_t* display_id_out); - -// Generate the human readable string from EDID obtained from |output|. -DISPLAY_UTIL_EXPORT std::string GetDisplayName(RROutput output); - -// Gets the overscan flag from |output| and stores to |flag|. Returns true if -// the flag is found. Otherwise returns false and doesn't touch |flag|. The -// output will produce overscan if |flag| is set to true, but the output may -// still produce overscan even though it returns true and |flag| is set to -// false. -DISPLAY_UTIL_EXPORT bool GetOutputOverscanFlag(RROutput output, bool* flag); +// Xrandr utility class to help get EDID information. +class DISPLAY_UTIL_EXPORT EDIDParserX11 { + public: + EDIDParserX11(XID output_id); + ~EDIDParserX11(); + + // Sets |out_display_id| to the display ID from the EDID of this output. + // Returns true if successful, false otherwise. + bool GetDisplayId(uint8_t index, int64_t* out_display_id) const; + + // Generate the human readable string from EDID obtained from |output|. + // Returns an empty string upon error. + std::string GetDisplayName() const; + + // Gets the overscan flag and stores to |out_flag|. Returns true if the flag + // is found. Otherwise returns false and doesn't touch |out_flag|. The output + // will produce overscan if |out_flag| is set to true, but the output may + // still produce overscan even though it returns true and |out_flag| is set to + // false. + bool GetOutputOverscanFlag(bool* out_flag) const; + + XID output_id() const { return output_id_; } + const std::vector<uint8_t>& edid() const { return edid_; } + + private: + XID output_id_; + + // This will be an empty vector upon failure to get the EDID from the + // |output_id_|. + std::vector<uint8_t> edid_; + + DISALLOW_COPY_AND_ASSIGN(EDIDParserX11); +}; } // namespace ui diff --git a/chromium/ui/display/win/display_info.cc b/chromium/ui/display/win/display_info.cc new file mode 100644 index 00000000000..1b78fb7266d --- /dev/null +++ b/chromium/ui/display/win/display_info.cc @@ -0,0 +1,60 @@ +// 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/display/win/display_info.h" + +#include "base/hash.h" +#include "base/strings/utf_string_conversions.h" + +namespace { + +gfx::Display::Rotation GetRotationForDevice(const wchar_t* device_name) { + DEVMODE mode; + ::ZeroMemory(&mode, sizeof(mode)); + mode.dmSize = sizeof(mode); + mode.dmDriverExtra = 0; + if (::EnumDisplaySettings(device_name, ENUM_CURRENT_SETTINGS, &mode)) { + switch (mode.dmDisplayOrientation) { + case DMDO_DEFAULT: + return gfx::Display::ROTATE_0; + case DMDO_90: + return gfx::Display::ROTATE_90; + case DMDO_180: + return gfx::Display::ROTATE_180; + case DMDO_270: + return gfx::Display::ROTATE_270; + default: + NOTREACHED(); + } + } + return gfx::Display::ROTATE_0; +} + +} // namespace + +namespace display { +namespace win { + +DisplayInfo::DisplayInfo(const MONITORINFOEX& monitor_info, + float device_scale_factor) + : DisplayInfo(monitor_info, + device_scale_factor, + GetRotationForDevice(monitor_info.szDevice)) {} + +DisplayInfo::DisplayInfo(const MONITORINFOEX& monitor_info, + float device_scale_factor, + gfx::Display::Rotation rotation) + : id_(DeviceIdFromDeviceName(monitor_info.szDevice)), + rotation_(rotation), + screen_rect_(monitor_info.rcMonitor), + screen_work_rect_(monitor_info.rcWork), + device_scale_factor_(device_scale_factor) {} + +// static +int64_t DisplayInfo::DeviceIdFromDeviceName(const wchar_t* device_name) { + return static_cast<int64_t>(base::Hash(base::WideToUTF8(device_name))); +} + +} // namespace win +} // namespace display diff --git a/chromium/ui/display/win/display_info.h b/chromium/ui/display/win/display_info.h new file mode 100644 index 00000000000..d9abdc3f2a7 --- /dev/null +++ b/chromium/ui/display/win/display_info.h @@ -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. + +#ifndef UI_DISPLAY_WIN_DISPLAY_INFO_H_ +#define UI_DISPLAY_WIN_DISPLAY_INFO_H_ + +#include <windows.h> +#include <stdint.h> + +#include "ui/display/display_export.h" +#include "ui/gfx/display.h" + +namespace display { +namespace win { + +// Gathers the parameters necessary to create a display::win::ScreenWinDisplay. +class DISPLAY_EXPORT DisplayInfo final { + public: + DisplayInfo(const MONITORINFOEX& monitor_info, float device_scale_factor); + DisplayInfo(const MONITORINFOEX& monitor_info, + float device_scale_factor, + gfx::Display::Rotation rotation); + + static int64_t DeviceIdFromDeviceName(const wchar_t* device_name); + + int64_t id() const { return id_; } + gfx::Display::Rotation rotation() const { return rotation_; } + const gfx::Rect& screen_rect() const { return screen_rect_; } + const gfx::Rect& screen_work_rect() const { return screen_work_rect_; } + float device_scale_factor() const { return device_scale_factor_; } + + private: + int64_t id_; + gfx::Display::Rotation rotation_; + gfx::Rect screen_rect_; + gfx::Rect screen_work_rect_; + float device_scale_factor_; +}; + +} // namespace win +} // namespace display + +#endif // UI_DISPLAY_WIN_DISPLAY_INFO_H_ diff --git a/chromium/ui/display/win/screen_win.cc b/chromium/ui/display/win/screen_win.cc new file mode 100644 index 00000000000..96d880f4d51 --- /dev/null +++ b/chromium/ui/display/win/screen_win.cc @@ -0,0 +1,242 @@ +// 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/display/win/screen_win.h" + +#include <windows.h> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "ui/display/win/display_info.h" +#include "ui/display/win/screen_win_display.h" +#include "ui/gfx/display.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/win/dpi.h" + +namespace display { +namespace win { + +namespace { + +std::vector<ScreenWinDisplay> DisplayInfosToScreenWinDisplays( + const std::vector<DisplayInfo>& display_infos) { + std::vector<ScreenWinDisplay> screen_win_displays; + for (const auto& display_info : display_infos) + screen_win_displays.push_back(ScreenWinDisplay(display_info)); + + return screen_win_displays; +} + +std::vector<gfx::Display> ScreenWinDisplaysToDisplays( + const std::vector<ScreenWinDisplay>& screen_win_displays) { + std::vector<gfx::Display> displays; + for (const auto& screen_win_display : screen_win_displays) + displays.push_back(screen_win_display.display()); + + return displays; +} + +MONITORINFOEX MonitorInfoFromHMONITOR(HMONITOR monitor) { + MONITORINFOEX monitor_info; + ::ZeroMemory(&monitor_info, sizeof(monitor_info)); + monitor_info.cbSize = sizeof(monitor_info); + ::GetMonitorInfo(monitor, &monitor_info); + return monitor_info; +} + +BOOL CALLBACK EnumMonitorCallback(HMONITOR monitor, + HDC hdc, + LPRECT rect, + LPARAM data) { + std::vector<DisplayInfo>* display_infos = + reinterpret_cast<std::vector<DisplayInfo>*>(data); + DCHECK(display_infos); + display_infos->push_back(DisplayInfo(MonitorInfoFromHMONITOR(monitor), + gfx::GetDPIScale())); + return TRUE; +} + +std::vector<DisplayInfo> GetDisplayInfosFromSystem() { + std::vector<DisplayInfo> display_infos; + EnumDisplayMonitors(nullptr, nullptr, EnumMonitorCallback, + reinterpret_cast<LPARAM>(&display_infos)); + DCHECK_EQ(static_cast<size_t>(::GetSystemMetrics(SM_CMONITORS)), + display_infos.size()); + return display_infos; +} + +} // namespace + +ScreenWin::ScreenWin() { + Initialize(); +} + +ScreenWin::~ScreenWin() = default; + +HWND ScreenWin::GetHWNDFromNativeView(gfx::NativeView window) const { + NOTREACHED(); + return nullptr; +} + +gfx::NativeWindow ScreenWin::GetNativeWindowFromHWND(HWND hwnd) const { + NOTREACHED(); + return nullptr; +} + +gfx::Point ScreenWin::GetCursorScreenPoint() { + POINT pt; + ::GetCursorPos(&pt); + gfx::Point cursor_pos_pixels(pt); + return gfx::win::ScreenToDIPPoint(cursor_pos_pixels); +} + +gfx::NativeWindow ScreenWin::GetWindowUnderCursor() { + POINT cursor_loc; + HWND hwnd = + ::GetCursorPos(&cursor_loc) ? ::WindowFromPoint(cursor_loc) : nullptr; + return GetNativeWindowFromHWND(hwnd); +} + +gfx::NativeWindow ScreenWin::GetWindowAtScreenPoint(const gfx::Point& point) { + gfx::Point point_in_pixels = gfx::win::DIPToScreenPoint(point); + return GetNativeWindowFromHWND(WindowFromPoint(point_in_pixels.ToPOINT())); +} + +int ScreenWin::GetNumDisplays() const { + return static_cast<int>(screen_win_displays_.size()); +} + +std::vector<gfx::Display> ScreenWin::GetAllDisplays() const { + return ScreenWinDisplaysToDisplays(screen_win_displays_); +} + +gfx::Display ScreenWin::GetDisplayNearestWindow(gfx::NativeView window) const { + HWND window_hwnd = GetHWNDFromNativeView(window); + if (!window_hwnd) { + // When |window| isn't rooted to a display, we should just return the + // default display so we get some correct display information like the + // scaling factor. + return GetPrimaryDisplay(); + } + ScreenWinDisplay screen_win_display = + GetScreenWinDisplayNearestHWND(window_hwnd); + return screen_win_display.display(); +} + +gfx::Display ScreenWin::GetDisplayNearestPoint(const gfx::Point& point) const { + gfx::Point screen_point(gfx::win::DIPToScreenPoint(point)); + ScreenWinDisplay screen_win_display = + GetScreenWinDisplayNearestScreenPoint(screen_point); + return screen_win_display.display(); +} + +gfx::Display ScreenWin::GetDisplayMatching(const gfx::Rect& match_rect) const { + ScreenWinDisplay screen_win_display = + GetScreenWinDisplayNearestScreenRect(match_rect); + return screen_win_display.display(); +} + +gfx::Display ScreenWin::GetPrimaryDisplay() const { + return GetPrimaryScreenWinDisplay().display(); +} + +void ScreenWin::AddObserver(gfx::DisplayObserver* observer) { + change_notifier_.AddObserver(observer); +} + +void ScreenWin::RemoveObserver(gfx::DisplayObserver* observer) { + change_notifier_.RemoveObserver(observer); +} + +void ScreenWin::UpdateFromDisplayInfos( + const std::vector<DisplayInfo>& display_infos) { + screen_win_displays_ = DisplayInfosToScreenWinDisplays(display_infos); +} + +void ScreenWin::Initialize() { + singleton_hwnd_observer_.reset( + new gfx::SingletonHwndObserver( + base::Bind(&ScreenWin::OnWndProc, base::Unretained(this)))); + UpdateFromDisplayInfos(GetDisplayInfosFromSystem()); +} + +MONITORINFOEX ScreenWin::MonitorInfoFromScreenPoint( + const gfx::Point& screen_point) const { + POINT initial_loc = { screen_point.x(), screen_point.y() }; + return MonitorInfoFromHMONITOR(::MonitorFromPoint(initial_loc, + MONITOR_DEFAULTTONEAREST)); +} + +MONITORINFOEX ScreenWin::MonitorInfoFromScreenRect(const gfx::Rect& screen_rect) + const { + RECT win_rect = screen_rect.ToRECT(); + return MonitorInfoFromHMONITOR(::MonitorFromRect(&win_rect, + MONITOR_DEFAULTTONEAREST)); +} + +MONITORINFOEX ScreenWin::MonitorInfoFromWindow(HWND hwnd, + DWORD default_options) const { + return MonitorInfoFromHMONITOR(::MonitorFromWindow(hwnd, default_options)); +} + +HWND ScreenWin::GetRootWindow(HWND hwnd) const { + return ::GetAncestor(hwnd, GA_ROOT); +} + +void ScreenWin::OnWndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message != WM_DISPLAYCHANGE) + return; + + std::vector<gfx::Display> old_displays = GetAllDisplays(); + UpdateFromDisplayInfos(GetDisplayInfosFromSystem()); + change_notifier_.NotifyDisplaysChanged(old_displays, GetAllDisplays()); +} + +ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestHWND(HWND hwnd) + const { + return GetScreenWinDisplay(MonitorInfoFromWindow(hwnd, + MONITOR_DEFAULTTONEAREST)); +} + +ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestScreenRect( + const gfx::Rect& screen_rect) const { + return GetScreenWinDisplay(MonitorInfoFromScreenRect(screen_rect)); +} + +ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestScreenPoint( + const gfx::Point& screen_point) const { + return GetScreenWinDisplay(MonitorInfoFromScreenPoint(screen_point)); +} + +ScreenWinDisplay ScreenWin::GetPrimaryScreenWinDisplay() const { + MONITORINFOEX monitor_info = MonitorInfoFromWindow(nullptr, + MONITOR_DEFAULTTOPRIMARY); + ScreenWinDisplay screen_win_display = GetScreenWinDisplay(monitor_info); + gfx::Display display = screen_win_display.display(); + // The Windows primary monitor is defined to have an origin of (0, 0). + DCHECK_EQ(0, display.bounds().origin().x()); + DCHECK_EQ(0, display.bounds().origin().y()); + return screen_win_display; +} + +ScreenWinDisplay ScreenWin::GetScreenWinDisplay( + const MONITORINFOEX& monitor_info) const { + int64_t id = DisplayInfo::DeviceIdFromDeviceName(monitor_info.szDevice); + for (const auto& screen_win_display : screen_win_displays_) { + if (screen_win_display.display().id() == id) + return screen_win_display; + } + // There is 1:1 correspondence between MONITORINFOEX and ScreenWinDisplay. + // If we make it here, it means we have no displays and we should hand out the + // default display. + DCHECK_EQ(screen_win_displays_.size(), 0u); + return ScreenWinDisplay(); +} + +} // namespace win +} // namespace display diff --git a/chromium/ui/display/win/screen_win.h b/chromium/ui/display/win/screen_win.h new file mode 100644 index 00000000000..da18ed1c7ae --- /dev/null +++ b/chromium/ui/display/win/screen_win.h @@ -0,0 +1,102 @@ +// 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_DISPLAY_WIN_SCREEN_WIN_H_ +#define UI_DISPLAY_WIN_SCREEN_WIN_H_ + +#include <windows.h> + +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/display/display_export.h" +#include "ui/gfx/display_change_notifier.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/screen.h" +#include "ui/gfx/win/singleton_hwnd_observer.h" + +namespace gfx { +class Display; +class Point; +class Rect; +} // namespace gfx + +namespace display { +namespace win { + +class DisplayInfo; +class ScreenWinDisplay; + +class DISPLAY_EXPORT ScreenWin : public gfx::Screen { + public: + ScreenWin(); + ~ScreenWin() override; + + // Returns the HWND associated with the NativeView. + virtual HWND GetHWNDFromNativeView(gfx::NativeView window) const; + + // Returns the NativeView associated with the HWND. + virtual gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const; + + protected: + // gfx::Screen: + gfx::Point GetCursorScreenPoint() override; + gfx::NativeWindow GetWindowUnderCursor() override; + gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override; + int GetNumDisplays() const override; + std::vector<gfx::Display> GetAllDisplays() const override; + gfx::Display GetDisplayNearestWindow(gfx::NativeView window) const override; + gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override; + gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override; + gfx::Display GetPrimaryDisplay() const override; + void AddObserver(gfx::DisplayObserver* observer) override; + void RemoveObserver(gfx::DisplayObserver* observer) override; + + void UpdateFromDisplayInfos(const std::vector<DisplayInfo>& display_infos); + + // Virtual to support mocking by unit tests. + virtual void Initialize(); + virtual MONITORINFOEX MonitorInfoFromScreenPoint( + const gfx::Point& screen_point) const; + virtual MONITORINFOEX MonitorInfoFromScreenRect(const gfx::Rect& screen_rect) + const; + virtual MONITORINFOEX MonitorInfoFromWindow(HWND hwnd, DWORD default_options) + const; + virtual HWND GetRootWindow(HWND hwnd) const; + + private: + void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + // Returns the ScreenWinDisplay closest to or enclosing |hwnd|. + ScreenWinDisplay GetScreenWinDisplayNearestHWND(HWND hwnd) const; + + // Returns the ScreenWinDisplay closest to or enclosing |screen_rect|. + ScreenWinDisplay GetScreenWinDisplayNearestScreenRect( + const gfx::Rect& screen_rect) const; + + // Returns the ScreenWinDisplay closest to or enclosing |screen_point|. + ScreenWinDisplay GetScreenWinDisplayNearestScreenPoint( + const gfx::Point& screen_point) const; + + // Returns the ScreenWinDisplay corresponding to the primary monitor. + ScreenWinDisplay GetPrimaryScreenWinDisplay() const; + + ScreenWinDisplay GetScreenWinDisplay(const MONITORINFOEX& monitor_info) const; + + // Helper implementing the DisplayObserver handling. + gfx::DisplayChangeNotifier change_notifier_; + + scoped_ptr<gfx::SingletonHwndObserver> singleton_hwnd_observer_; + + // Current list of ScreenWinDisplays. + std::vector<ScreenWinDisplay> screen_win_displays_; + + DISALLOW_COPY_AND_ASSIGN(ScreenWin); +}; + +} // namespace win +} // namespace display + +#endif // UI_DISPLAY_WIN_SCREEN_WIN_H_ diff --git a/chromium/ui/display/win/screen_win_display.cc b/chromium/ui/display/win/screen_win_display.cc new file mode 100644 index 00000000000..38104bbc3ff --- /dev/null +++ b/chromium/ui/display/win/screen_win_display.cc @@ -0,0 +1,37 @@ +// 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/display/win/screen_win_display.h" + +#include "ui/display/win/display_info.h" +#include "ui/gfx/win/dpi.h" + +namespace display { +namespace win { + +namespace { + +gfx::Display CreateDisplayFromDisplayInfo(const DisplayInfo& display_info) { + gfx::Display display(display_info.id()); + gfx::Rect dip_screen_bounds( + gfx::win::ScreenToDIPRect(display_info.screen_rect())); + display.set_bounds(dip_screen_bounds); + display.set_work_area( + gfx::win::ScreenToDIPRect(display_info.screen_work_rect())); + display.SetScaleAndBounds(display_info.device_scale_factor(), + display_info.screen_rect()); + display.set_rotation(display_info.rotation()); + return display; +} + +} // namespace + +ScreenWinDisplay::ScreenWinDisplay() = default; + +ScreenWinDisplay::ScreenWinDisplay(const DisplayInfo& display_info) + : display_(CreateDisplayFromDisplayInfo(display_info)), + pixel_bounds_(display_info.screen_rect()) {} + +} // namespace win +} // namespace display diff --git a/chromium/ui/display/win/screen_win_display.h b/chromium/ui/display/win/screen_win_display.h new file mode 100644 index 00000000000..901f2d7e855 --- /dev/null +++ b/chromium/ui/display/win/screen_win_display.h @@ -0,0 +1,36 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_DISPLAY_WIN_SCREEN_WIN_DISPLAY_H_ +#define UI_DISPLAY_WIN_SCREEN_WIN_DISPLAY_H_ + +#include <windows.h> + +#include "ui/gfx/display.h" +#include "ui/gfx/geometry/rect.h" + +namespace display { +namespace win { + +class DisplayInfo; + +// A display used by gfx::ScreenWin. +// It holds a display and additional parameters used for DPI calculations. +class ScreenWinDisplay final { + public: + ScreenWinDisplay(); + explicit ScreenWinDisplay(const DisplayInfo& display_info); + + const gfx::Display& display() const { return display_; } + const gfx::Rect& pixel_bounds() const { return pixel_bounds_; } + + private: + gfx::Display display_; + gfx::Rect pixel_bounds_; +}; + +} // namespace win +} // namespace display + +#endif // UI_DISPLAY_WIN_SCREEN_WIN_DISPLAY_H_ diff --git a/chromium/ui/display/win/screen_win_unittest.cc b/chromium/ui/display/win/screen_win_unittest.cc new file mode 100644 index 00000000000..2542167c285 --- /dev/null +++ b/chromium/ui/display/win/screen_win_unittest.cc @@ -0,0 +1,784 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/display/win/screen_win.h" + +#include <windows.h> +#include <inttypes.h> +#include <stddef.h> + +#include <cwchar> +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/win/display_info.h" +#include "ui/display/win/screen_win_display.h" +#include "ui/gfx/display.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/screen.h" +#include "ui/gfx/test/display_util.h" +#include "ui/gfx/win/dpi.h" + +namespace display { +namespace win { + +namespace { + +MONITORINFOEX CreateMonitorInfo(gfx::Rect monitor, + gfx::Rect work, + std::wstring device_name) { + MONITORINFOEX monitor_info; + ::ZeroMemory(&monitor_info, sizeof(monitor_info)); + monitor_info.cbSize = sizeof(monitor_info); + monitor_info.rcMonitor = monitor.ToRECT(); + monitor_info.rcWork = work.ToRECT(); + size_t device_char_count = ARRAYSIZE(monitor_info.szDevice); + wcsncpy(monitor_info.szDevice, device_name.c_str(), device_char_count); + monitor_info.szDevice[device_char_count-1] = L'\0'; + return monitor_info; +} + +class TestScreenWin : public ScreenWin { + public: + TestScreenWin(const std::vector<DisplayInfo>& display_infos, + const std::vector<MONITORINFOEX>& monitor_infos, + const std::unordered_map<HWND, gfx::Rect>& hwnd_map) + : monitor_infos_(monitor_infos), + hwnd_map_(hwnd_map) { + UpdateFromDisplayInfos(display_infos); + } + + ~TestScreenWin() override = default; + + protected: + // display::win::ScreenWin: + HWND GetHWNDFromNativeView(gfx::NativeView window) const override { + // NativeView is only used as an identifier in this tests, so interchange + // NativeView with an HWND for convenience. + return reinterpret_cast<HWND>(window); + } + + gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override { + // NativeWindow is only used as an identifier in this tests, so interchange + // an HWND for a NativeWindow for convenience. + return reinterpret_cast<gfx::NativeWindow>(hwnd); + } + + private: + void Initialize() override {} + + MONITORINFOEX MonitorInfoFromScreenPoint(const gfx::Point& screen_point) const + override { + for (const MONITORINFOEX& monitor_info : monitor_infos_) { + if (gfx::Rect(monitor_info.rcMonitor).Contains(screen_point)) + return monitor_info; + } + NOTREACHED(); + return monitor_infos_[0]; + } + + MONITORINFOEX MonitorInfoFromScreenRect(const gfx::Rect& screen_rect) const + override { + MONITORINFOEX candidate = monitor_infos_[0]; + int largest_area = 0; + for (const MONITORINFOEX& monitor_info : monitor_infos_) { + gfx::Rect bounds(monitor_info.rcMonitor); + if (bounds.Intersects(screen_rect)) { + bounds.Intersect(screen_rect); + int area = bounds.height() * bounds.width(); + if (largest_area < area) { + candidate = monitor_info; + largest_area = area; + } + } + } + EXPECT_NE(largest_area, 0); + return candidate; + } + + MONITORINFOEX MonitorInfoFromWindow(HWND hwnd, DWORD default_options) + const override { + auto search = hwnd_map_.find(hwnd); + if (search != hwnd_map_.end()) + return MonitorInfoFromScreenRect(search->second); + + EXPECT_EQ(default_options, static_cast<DWORD>(MONITOR_DEFAULTTOPRIMARY)); + for (const auto& monitor_info : monitor_infos_) { + if (monitor_info.rcMonitor.left == 0 && + monitor_info.rcMonitor.top == 0) { + return monitor_info; + } + } + NOTREACHED(); + return monitor_infos_[0]; + } + + HWND GetRootWindow(HWND hwnd) const override { + return hwnd; + } + + std::vector<MONITORINFOEX> monitor_infos_; + std::unordered_map<HWND, gfx::Rect> hwnd_map_; + + DISALLOW_COPY_AND_ASSIGN(TestScreenWin); +}; + +gfx::Screen* GetScreen() { + return gfx::Screen::GetScreen(); +} + +// Allows tests to specify the screen and associated state. +class TestScreenWinInitializer { + public: + virtual void AddMonitor(const gfx::Rect& pixel_bounds, + const gfx::Rect& pixel_work, + const wchar_t* device_name, + float device_scale_factor) = 0; + + virtual HWND CreateFakeHwnd(const gfx::Rect& bounds) = 0; +}; + +class TestScreenWinManager : public TestScreenWinInitializer { + public: + TestScreenWinManager() = default; + + ~TestScreenWinManager() { + gfx::Screen::SetScreenInstance(nullptr); + } + + void AddMonitor(const gfx::Rect& pixel_bounds, + const gfx::Rect& pixel_work, + const wchar_t* device_name, + float device_scale_factor) override { + MONITORINFOEX monitor_info = CreateMonitorInfo(pixel_bounds, + pixel_work, + device_name); + monitor_infos_.push_back(monitor_info); + display_infos_.push_back(DisplayInfo(monitor_info, + device_scale_factor, + gfx::Display::ROTATE_0)); + } + + HWND CreateFakeHwnd(const gfx::Rect& bounds) override { + EXPECT_EQ(screen_win_, nullptr); + hwnd_map_.insert(std::pair<HWND, gfx::Rect>(++hwndLast_, bounds)); + return hwndLast_; + } + + void InitializeScreenWin() { + ASSERT_EQ(screen_win_, nullptr); + screen_win_.reset(new TestScreenWin(display_infos_, + monitor_infos_, + hwnd_map_)); + gfx::Screen::SetScreenInstance(screen_win_.get()); + } + + ScreenWin* GetScreenWin() { + return screen_win_.get(); + } + + private: + HWND hwndLast_ = nullptr; + scoped_ptr<ScreenWin> screen_win_; + std::vector<MONITORINFOEX> monitor_infos_; + std::vector<DisplayInfo> display_infos_; + std::unordered_map<HWND, gfx::Rect> hwnd_map_; + + DISALLOW_COPY_AND_ASSIGN(TestScreenWinManager); +}; + +class ScreenWinTest : public testing::Test { + protected: + ScreenWinTest() = default; + + void SetUp() override { + testing::Test::SetUp(); + gfx::SetDefaultDeviceScaleFactor(1.0); + screen_win_initializer_.reset(new TestScreenWinManager()); + SetUpScreen(screen_win_initializer_.get()); + screen_win_initializer_->InitializeScreenWin(); + } + + void TearDown() override { + screen_win_initializer_.reset(); + gfx::SetDefaultDeviceScaleFactor(1.0); + testing::Test::TearDown(); + } + + virtual void SetUpScreen(TestScreenWinInitializer* initializer) = 0; + + gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const { + ScreenWin* screen_win = screen_win_initializer_->GetScreenWin(); + return screen_win->GetNativeWindowFromHWND(hwnd);; + } + + private: + scoped_ptr<TestScreenWinManager> screen_win_initializer_; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTest); +}; + +// Single Display of 1.0 Device Scale Factor. +class ScreenWinTestSingleDisplay1x : public ScreenWinTest { + public: + ScreenWinTestSingleDisplay1x() = default; + + void SetUpScreen(TestScreenWinInitializer* initializer) override { + initializer->AddMonitor(gfx::Rect(0, 0, 1920, 1200), + gfx::Rect(0, 0, 1920, 1100), + L"primary", + 1.0); + fake_hwnd_ = initializer->CreateFakeHwnd(gfx::Rect(0, 0, 1920, 1100)); + } + + HWND GetFakeHwnd() { + return fake_hwnd_; + } + + private: + HWND fake_hwnd_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTestSingleDisplay1x); +}; + +TEST_F(ScreenWinTestSingleDisplay1x, GetDisplays) { + std::vector<gfx::Display> displays = GetScreen()->GetAllDisplays(); + ASSERT_EQ(1u, displays.size()); + EXPECT_EQ(gfx::Rect(0, 0, 1920, 1200), displays[0].bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 1920, 1100), displays[0].work_area()); +} + +TEST_F(ScreenWinTestSingleDisplay1x, GetNumDisplays) { + EXPECT_EQ(1, GetScreen()->GetNumDisplays()); +} + +TEST_F(ScreenWinTestSingleDisplay1x, GetDisplayNearestWindowPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + EXPECT_EQ(screen->GetPrimaryDisplay(), + screen->GetDisplayNearestWindow(nullptr)); +} + +TEST_F(ScreenWinTestSingleDisplay1x, GetDisplayNearestWindow) { + gfx::Screen* screen = GetScreen(); + gfx::NativeWindow native_window = GetNativeWindowFromHWND(GetFakeHwnd()); + EXPECT_EQ(screen->GetAllDisplays()[0], + screen->GetDisplayNearestWindow(native_window)); +} + +TEST_F(ScreenWinTestSingleDisplay1x, GetDisplayNearestPoint) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(0, 0))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(250, 952))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(1919, 1199))); +} + +TEST_F(ScreenWinTestSingleDisplay1x, GetDisplayMatching) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100))); + EXPECT_EQ(display, + screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100))); +} + +TEST_F(ScreenWinTestSingleDisplay1x, GetPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + EXPECT_EQ(gfx::Point(0, 0), screen->GetPrimaryDisplay().bounds().origin()); +} + +// Single Display of 1.25 Device Scale Factor. +class ScreenWinTestSingleDisplay1_25x : public ScreenWinTest { + public: + ScreenWinTestSingleDisplay1_25x() = default; + + void SetUpScreen(TestScreenWinInitializer* initializer) override { + gfx::SetDefaultDeviceScaleFactor(1.25); + // Add Monitor of Scale Factor 1.0 since gfx::GetDPIScale performs the + // clamping and not ScreenWin. + initializer->AddMonitor(gfx::Rect(0, 0, 1920, 1200), + gfx::Rect(0, 0, 1920, 1100), + L"primary", + 1.0); + fake_hwnd_ = initializer->CreateFakeHwnd(gfx::Rect(0, 0, 1920, 1100)); + } + + HWND GetFakeHwnd() { + return fake_hwnd_; + } + + private: + HWND fake_hwnd_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTestSingleDisplay1_25x); +}; + +TEST_F(ScreenWinTestSingleDisplay1_25x, GetDisplays) { + std::vector<gfx::Display> displays = GetScreen()->GetAllDisplays(); + ASSERT_EQ(1u, displays.size()); + // On Windows, scale factors of 1.25 or lower are clamped to 1.0. + EXPECT_EQ(gfx::Rect(0, 0, 1920, 1200), displays[0].bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 1920, 1100), displays[0].work_area()); +} + +TEST_F(ScreenWinTestSingleDisplay1_25x, GetDisplayNearestWindow) { + gfx::Screen* screen = GetScreen(); + gfx::NativeWindow native_window = GetNativeWindowFromHWND(GetFakeHwnd()); + EXPECT_EQ(screen->GetAllDisplays()[0], + screen->GetDisplayNearestWindow(native_window)); +} + +TEST_F(ScreenWinTestSingleDisplay1_25x, GetDisplayNearestPoint) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(0, 0))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(250, 952))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(1919, 1199))); +} + +TEST_F(ScreenWinTestSingleDisplay1_25x, GetDisplayMatching) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100))); + EXPECT_EQ(display, + screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100))); +} +TEST_F(ScreenWinTestSingleDisplay1_25x, GetPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + EXPECT_EQ(gfx::Point(0, 0), screen->GetPrimaryDisplay().bounds().origin()); +} + +// Single Display of 1.25 Device Scale Factor. +class ScreenWinTestSingleDisplay1_5x : public ScreenWinTest { + public: + ScreenWinTestSingleDisplay1_5x() = default; + + void SetUpScreen(TestScreenWinInitializer* initializer) override { + gfx::SetDefaultDeviceScaleFactor(1.5); + initializer->AddMonitor(gfx::Rect(0, 0, 1920, 1200), + gfx::Rect(0, 0, 1920, 1100), + L"primary", + 1.5); + fake_hwnd_ = initializer->CreateFakeHwnd(gfx::Rect(0, 0, 1920, 1100)); + } + + HWND GetFakeHwnd() { + return fake_hwnd_; + } + + private: + HWND fake_hwnd_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTestSingleDisplay1_5x); +}; + +TEST_F(ScreenWinTestSingleDisplay1_5x, GetDisplays) { + std::vector<gfx::Display> displays = GetScreen()->GetAllDisplays(); + ASSERT_EQ(1u, displays.size()); + EXPECT_EQ(gfx::Rect(0, 0, 1280, 800), displays[0].bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 1280, 734), displays[0].work_area()); +} + +TEST_F(ScreenWinTestSingleDisplay1_5x, GetDisplayNearestWindow) { + gfx::Screen* screen = GetScreen(); + gfx::NativeWindow native_window = GetNativeWindowFromHWND(GetFakeHwnd()); + EXPECT_EQ(screen->GetAllDisplays()[0], + screen->GetDisplayNearestWindow(native_window)); +} + +TEST_F(ScreenWinTestSingleDisplay1_5x, GetDisplayNearestPoint) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(0, 0))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(250, 524))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(1279, 733))); +} + +TEST_F(ScreenWinTestSingleDisplay1_5x, GetDisplayMatching) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100))); + EXPECT_EQ(display, + screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100))); +} +TEST_F(ScreenWinTestSingleDisplay1_5x, GetPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + EXPECT_EQ(gfx::Point(0, 0), screen->GetPrimaryDisplay().bounds().origin()); +} + + +// Single Display of 2.0 Device Scale Factor. +class ScreenWinTestSingleDisplay2x : public ScreenWinTest { + public: + ScreenWinTestSingleDisplay2x() = default; + + void SetUpScreen(TestScreenWinInitializer* initializer) override { + gfx::SetDefaultDeviceScaleFactor(2.0); + initializer->AddMonitor(gfx::Rect(0, 0, 1920, 1200), + gfx::Rect(0, 0, 1920, 1100), + L"primary", + 2.0); + fake_hwnd_ = initializer->CreateFakeHwnd(gfx::Rect(0, 0, 1920, 1100)); + } + + HWND GetFakeHwnd() { + return fake_hwnd_; + } + + private: + HWND fake_hwnd_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTestSingleDisplay2x); +}; + +TEST_F(ScreenWinTestSingleDisplay2x, GetDisplays) { + std::vector<gfx::Display> displays = GetScreen()->GetAllDisplays(); + ASSERT_EQ(1u, displays.size()); + EXPECT_EQ(gfx::Rect(0, 0, 960, 600), displays[0].bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 960, 550), displays[0].work_area()); +} + +TEST_F(ScreenWinTestSingleDisplay2x, GetDisplayNearestWindow) { + gfx::Screen* screen = GetScreen(); + gfx::NativeWindow native_window = GetNativeWindowFromHWND(GetFakeHwnd()); + EXPECT_EQ(screen->GetAllDisplays()[0], + screen->GetDisplayNearestWindow(native_window)); +} + +TEST_F(ScreenWinTestSingleDisplay2x, GetDisplayNearestPoint) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(0, 0))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(125, 476))); + EXPECT_EQ(display, screen->GetDisplayNearestPoint(gfx::Point(959, 599))); +} + +TEST_F(ScreenWinTestSingleDisplay2x, GetDisplayMatching) { + gfx::Screen* screen = GetScreen(); + gfx::Display display = screen->GetAllDisplays()[0]; + EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100))); + EXPECT_EQ(display, + screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100))); +} + +// Two Displays of 1.0 Device Scale Factor. +class ScreenWinTestTwoDisplays1x : public ScreenWinTest { + public: + ScreenWinTestTwoDisplays1x() = default; + + void SetUpScreen(TestScreenWinInitializer* initializer) override { + initializer->AddMonitor(gfx::Rect(0, 0, 1920, 1200), + gfx::Rect(0, 0, 1920, 1100), + L"primary", + 1.0); + initializer->AddMonitor(gfx::Rect(1920, 0, 800, 600), + gfx::Rect(1920, 0, 800, 600), + L"secondary", + 1.0); + fake_hwnd_left_ = initializer->CreateFakeHwnd(gfx::Rect(0, 0, 1920, 1100)); + fake_hwnd_right_ = + initializer->CreateFakeHwnd(gfx::Rect(1920, 0, 800, 600)); + } + + HWND GetLeftFakeHwnd() { + return fake_hwnd_left_; + } + + HWND GetRightFakeHwnd() { + return fake_hwnd_right_; + } + + private: + HWND fake_hwnd_left_ = nullptr; + HWND fake_hwnd_right_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTestTwoDisplays1x); +}; + +TEST_F(ScreenWinTestTwoDisplays1x, GetDisplays) { + std::vector<gfx::Display> displays = GetScreen()->GetAllDisplays(); + ASSERT_EQ(2u, displays.size()); + EXPECT_EQ(gfx::Rect(0, 0, 1920, 1200), displays[0].bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 1920, 1100), displays[0].work_area()); + EXPECT_EQ(gfx::Rect(1920, 0, 800, 600), displays[1].bounds()); + EXPECT_EQ(gfx::Rect(1920, 0, 800, 600), displays[1].work_area()); +} + +TEST_F(ScreenWinTestTwoDisplays1x, GetNumDisplays) { + EXPECT_EQ(2, GetScreen()->GetNumDisplays()); +} + +TEST_F(ScreenWinTestTwoDisplays1x, GetDisplayNearestWindowPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + EXPECT_EQ(screen->GetPrimaryDisplay(), + screen->GetDisplayNearestWindow(nullptr)); +} + +TEST_F(ScreenWinTestTwoDisplays1x, GetDisplayNearestWindow) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + gfx::NativeWindow left_window = GetNativeWindowFromHWND(GetLeftFakeHwnd()); + EXPECT_EQ(left_display, screen->GetDisplayNearestWindow(left_window)); + + gfx::NativeWindow right_window = GetNativeWindowFromHWND(GetRightFakeHwnd()); + EXPECT_EQ(right_display, screen->GetDisplayNearestWindow(right_window)); +} + +TEST_F(ScreenWinTestTwoDisplays1x, GetDisplayNearestPoint) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + EXPECT_EQ(left_display, screen->GetDisplayNearestPoint(gfx::Point(0, 0))); + EXPECT_EQ(left_display, screen->GetDisplayNearestPoint(gfx::Point(250, 952))); + EXPECT_EQ(left_display, + screen->GetDisplayNearestPoint(gfx::Point(1919, 1199))); + + EXPECT_EQ(right_display, screen->GetDisplayNearestPoint(gfx::Point(1920, 0))); + EXPECT_EQ(right_display, + screen->GetDisplayNearestPoint(gfx::Point(2000, 400))); + EXPECT_EQ(right_display, + screen->GetDisplayNearestPoint(gfx::Point(2719, 599))); +} + +TEST_F(ScreenWinTestTwoDisplays1x, GetDisplayMatching) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + EXPECT_EQ(left_display, + screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100))); + EXPECT_EQ(left_display, + screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100))); + + EXPECT_EQ(right_display, + screen->GetDisplayMatching(gfx::Rect(1920, 0, 100, 100))); + EXPECT_EQ(right_display, + screen->GetDisplayMatching(gfx::Rect(2619, 499, 100, 100))); +} + +TEST_F(ScreenWinTestTwoDisplays1x, GetPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + gfx::Display primary = screen->GetPrimaryDisplay(); + EXPECT_EQ(gfx::Point(0, 0), primary.bounds().origin()); +} + +// Two Displays of 2.0 Device Scale Factor. +class ScreenWinTestTwoDisplays2x : public ScreenWinTest { + public: + ScreenWinTestTwoDisplays2x() = default; + + void SetUpScreen(TestScreenWinInitializer* initializer) override { + gfx::SetDefaultDeviceScaleFactor(2.0); + initializer->AddMonitor(gfx::Rect(0, 0, 1920, 1200), + gfx::Rect(0, 0, 1920, 1100), + L"primary", + 2.0); + initializer->AddMonitor(gfx::Rect(1920, 0, 800, 600), + gfx::Rect(1920, 0, 800, 600), + L"secondary", + 2.0); + fake_hwnd_left_ = initializer->CreateFakeHwnd(gfx::Rect(0, 0, 1920, 1100)); + fake_hwnd_right_ = + initializer->CreateFakeHwnd(gfx::Rect(1920, 0, 800, 600)); + } + + HWND GetLeftFakeHwnd() { + return fake_hwnd_left_; + } + + HWND GetRightFakeHwnd() { + return fake_hwnd_right_; + } + + private: + HWND fake_hwnd_left_ = nullptr; + HWND fake_hwnd_right_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTestTwoDisplays2x); +}; + +TEST_F(ScreenWinTestTwoDisplays2x, GetDisplays) { + std::vector<gfx::Display> displays = GetScreen()->GetAllDisplays(); + ASSERT_EQ(2u, displays.size()); + EXPECT_EQ(gfx::Rect(0, 0, 960, 600), displays[0].bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 960, 550), displays[0].work_area()); + EXPECT_EQ(gfx::Rect(960, 0, 400, 300), displays[1].bounds()); + EXPECT_EQ(gfx::Rect(960, 0, 400, 300), displays[1].work_area()); +} + +TEST_F(ScreenWinTestTwoDisplays2x, GetDisplayNearestWindowPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + EXPECT_EQ(screen->GetPrimaryDisplay(), + screen->GetDisplayNearestWindow(nullptr)); +} + +TEST_F(ScreenWinTestTwoDisplays2x, GetDisplayNearestWindow) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + gfx::NativeWindow left_window = GetNativeWindowFromHWND(GetLeftFakeHwnd()); + EXPECT_EQ(left_display, screen->GetDisplayNearestWindow(left_window)); + + gfx::NativeWindow right_window = GetNativeWindowFromHWND(GetRightFakeHwnd()); + EXPECT_EQ(right_display, screen->GetDisplayNearestWindow(right_window)); +} + +TEST_F(ScreenWinTestTwoDisplays2x, GetDisplayNearestPoint) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + EXPECT_EQ(left_display, screen->GetDisplayNearestPoint(gfx::Point(0, 0))); + EXPECT_EQ(left_display, screen->GetDisplayNearestPoint(gfx::Point(125, 476))); + EXPECT_EQ(left_display, + screen->GetDisplayNearestPoint(gfx::Point(959, 599))); + + EXPECT_EQ(right_display, screen->GetDisplayNearestPoint(gfx::Point(960, 0))); + EXPECT_EQ(right_display, + screen->GetDisplayNearestPoint(gfx::Point(1000, 200))); + EXPECT_EQ(right_display, + screen->GetDisplayNearestPoint(gfx::Point(1359, 299))); +} + +TEST_F(ScreenWinTestTwoDisplays2x, GetDisplayMatching) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + EXPECT_EQ(left_display, + screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100))); + EXPECT_EQ(left_display, + screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100))); + + EXPECT_EQ(right_display, + screen->GetDisplayMatching(gfx::Rect(1920, 0, 100, 100))); + EXPECT_EQ(right_display, + screen->GetDisplayMatching(gfx::Rect(2619, 499, 100, 100))); +} + +TEST_F(ScreenWinTestTwoDisplays2x, GetPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + gfx::Display primary = screen->GetPrimaryDisplay(); + EXPECT_EQ(gfx::Point(0, 0), primary.bounds().origin()); +} + +// Two Displays of 2.0 (Left) and 1.0 (Right) Device Scale Factor under +// Windows DPI Virtualization. Note that the displays do not form a euclidean +// space. +class ScreenWinTestTwoDisplays2x1xVirtualized : public ScreenWinTest { + public: + ScreenWinTestTwoDisplays2x1xVirtualized() = default; + + void SetUpScreen(TestScreenWinInitializer* initializer) override { + gfx::SetDefaultDeviceScaleFactor(2.0); + initializer->AddMonitor(gfx::Rect(0, 0, 3200, 1600), + gfx::Rect(0, 0, 3200, 1500), + L"primary", + 2.0); + initializer->AddMonitor(gfx::Rect(6400, 0, 3840, 2400), + gfx::Rect(6400, 0, 3840, 2400), + L"secondary", + 2.0); + fake_hwnd_left_ = initializer->CreateFakeHwnd(gfx::Rect(0, 0, 3200, 1500)); + fake_hwnd_right_ = + initializer->CreateFakeHwnd(gfx::Rect(6400, 0, 3840, 2400)); + } + + HWND GetLeftFakeHwnd() { + return fake_hwnd_left_; + } + + HWND GetRightFakeHwnd() { + return fake_hwnd_right_; + } + + private: + HWND fake_hwnd_left_ = nullptr; + HWND fake_hwnd_right_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScreenWinTestTwoDisplays2x1xVirtualized); +}; + +TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetDisplays) { + std::vector<gfx::Display> displays = GetScreen()->GetAllDisplays(); + ASSERT_EQ(2u, displays.size()); + EXPECT_EQ(gfx::Rect(0, 0, 1600, 800), displays[0].bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 1600, 750), displays[0].work_area()); + EXPECT_EQ(gfx::Rect(3200, 0, 1920, 1200), displays[1].bounds()); + EXPECT_EQ(gfx::Rect(3200, 0, 1920, 1200), displays[1].work_area()); +} + +TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetNumDisplays) { + EXPECT_EQ(2, GetScreen()->GetNumDisplays()); +} + +TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, + GetDisplayNearestWindowPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + EXPECT_EQ(screen->GetPrimaryDisplay(), + screen->GetDisplayNearestWindow(nullptr)); +} + +TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetDisplayNearestWindow) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + gfx::NativeWindow left_window = GetNativeWindowFromHWND(GetLeftFakeHwnd()); + EXPECT_EQ(left_display, screen->GetDisplayNearestWindow(left_window)); + + gfx::NativeWindow right_window = GetNativeWindowFromHWND(GetRightFakeHwnd()); + EXPECT_EQ(right_display, screen->GetDisplayNearestWindow(right_window)); +} + +TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetDisplayNearestPoint) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + EXPECT_EQ(left_display, screen->GetDisplayNearestPoint(gfx::Point(0, 0))); + EXPECT_EQ(left_display, screen->GetDisplayNearestPoint(gfx::Point(125, 476))); + EXPECT_EQ(left_display, + screen->GetDisplayNearestPoint(gfx::Point(1599, 799))); + + EXPECT_EQ(right_display, screen->GetDisplayNearestPoint(gfx::Point(3200, 0))); + EXPECT_EQ(right_display, + screen->GetDisplayNearestPoint(gfx::Point(4000, 400))); + EXPECT_EQ(right_display, + screen->GetDisplayNearestPoint(gfx::Point(5119, 1199))); +} + +TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetDisplayMatching) { + gfx::Screen* screen = GetScreen(); + const gfx::Display left_display = screen->GetAllDisplays()[0]; + const gfx::Display right_display = screen->GetAllDisplays()[1]; + + EXPECT_EQ(left_display, + screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100))); + EXPECT_EQ(left_display, + screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100))); + + EXPECT_EQ(right_display, + screen->GetDisplayMatching(gfx::Rect(6400, 0, 100, 100))); + EXPECT_EQ(right_display, + screen->GetDisplayMatching(gfx::Rect(10139, 2299, 100, 100))); +} + +TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetPrimaryDisplay) { + gfx::Screen* screen = GetScreen(); + gfx::Display primary = screen->GetPrimaryDisplay(); + EXPECT_EQ(gfx::Point(0, 0), primary.bounds().origin()); +} + +} // namespace + +} // namespace win +} // namespace display diff --git a/chromium/ui/events/BUILD.gn b/chromium/ui/events/BUILD.gn index a875264491b..20b75f2cbda 100644 --- a/chromium/ui/events/BUILD.gn +++ b/chromium/ui/events/BUILD.gn @@ -10,6 +10,10 @@ if (is_android) { import("//build/config/android/rules.gni") } +if (is_ios) { + import("//ios/build/config.gni") +} + static_library("dom_keycode_converter") { sources = [ "keycodes/dom/dom_code.h", @@ -57,13 +61,12 @@ component("events_base") { defines = [ "EVENTS_BASE_IMPLEMENTATION" ] deps = [ - ":dom_keycode_converter", "//base/third_party/dynamic_annotations", - "//ipc:param_traits", "//skia", ] public_deps = [ + ":dom_keycode_converter", "//base", "//ui/events/platform", "//ui/gfx", @@ -93,6 +96,10 @@ component("events_base") { "keycodes/xkb_keysym.h", ] } + + if (!is_ios) { + deps += [ "//ipc:param_traits" ] + } } component("events") { @@ -121,6 +128,8 @@ component("events") { "events_stub.cc", "gestures/gesture_recognizer_impl_mac.cc", "gestures/gesture_types.h", + "keycodes/platform_key_map_win.cc", + "keycodes/platform_key_map_win.h", "null_event_targeter.cc", "null_event_targeter.h", "scoped_target_handler.cc", @@ -201,10 +210,15 @@ component("events") { sources += [ "android/events_jni_registrar.cc", "android/events_jni_registrar.h", + "android/key_event_utils.cc", + "android/key_event_utils.h", "android/motion_event_android.cc", "android/motion_event_android.h", ] - deps += [ ":motionevent_jni_headers" ] + deps += [ + ":keyevent_jni_headers", + ":motionevent_jni_headers", + ] } } @@ -333,15 +347,6 @@ source_set("test_support") { } } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("events_unittests_run") { - testonly = true - deps = [ - ":events_unittests", - ] -} - test("events_unittests") { sources = [ "android/scroller_unittest.cc", @@ -360,12 +365,13 @@ test("events_unittests") { "gesture_detection/touch_disposition_gesture_filter_unittest.cc", "gesture_detection/velocity_tracker_unittest.cc", "gestures/fling_curve_unittest.cc", - "ipc/latency_info_param_traits_unittest.cc", "keycodes/dom/keycode_converter_unittest.cc", "keycodes/keyboard_code_conversion_unittest.cc", + "keycodes/platform_key_map_win_unittest.cc", "latency_info_unittest.cc", "platform/platform_event_source_unittest.cc", "scoped_target_handler_unittest.cc", + "win/event_utils_win_unittest.cc", ] deps = [ @@ -376,12 +382,11 @@ test("events_unittests") { ":test_support", "//base", "//base/test:run_all_unittests", - "//ipc:test_support", + "//base/test:test_support", "//skia", "//testing/gmock", "//testing/gtest", "//ui/events/devices", - "//ui/events/ipc:events_ipc", "//ui/events/platform", "//ui/gfx:test_support", ] @@ -391,12 +396,15 @@ test("events_unittests") { "blink/input_handler_proxy_unittest.cc", "blink/input_scroll_elasticity_controller_unittest.cc", "gestures/blink/web_gesture_curve_impl_unittest.cc", + "ipc/latency_info_param_traits_unittest.cc", ] deps += [ "//cc", + "//ipc:test_support", "//third_party/WebKit/public:blink_headers", "//ui/events/blink", "//ui/events/gestures/blink", + "//ui/events/ipc", ] } @@ -455,6 +463,10 @@ test("events_unittests") { if (is_android) { sources += [ "android/motion_event_android_unittest.cc" ] } + + if (is_ios) { + assert_no_deps = ios_assert_no_deps + } } if (is_android) { @@ -462,4 +474,9 @@ if (is_android) { jni_package = "ui" classes = [ "android/view/MotionEvent.class" ] } + + generate_jar_jni("keyevent_jni_headers") { + jni_package = "ui" + classes = [ "android/view/KeyEvent.class" ] + } } diff --git a/chromium/ui/events/android/key_event_utils.cc b/chromium/ui/events/android/key_event_utils.cc new file mode 100644 index 00000000000..af2f2c5b176 --- /dev/null +++ b/chromium/ui/events/android/key_event_utils.cc @@ -0,0 +1,32 @@ +// 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/events/android/key_event_utils.h" + +#include "jni/KeyEvent_jni.h" + +namespace ui { +namespace events { +namespace android { + +bool RegisterKeyEvent(JNIEnv* env) { + return JNI_KeyEvent::RegisterNativesImpl(env); +} + +base::android::ScopedJavaLocalRef<jobject> CreateKeyEvent(JNIEnv* env, + int action, + int key_code) { + return JNI_KeyEvent::Java_KeyEvent_ConstructorAVKE_I_I(env, action, key_code); +} + +int GetKeyEventUnicodeChar(JNIEnv* env, + const base::android::JavaRef<jobject>& event, + int meta_state) { + return static_cast<int>(JNI_KeyEvent::Java_KeyEvent_getUnicodeCharI_I( + env, event.obj(), meta_state)); +} + +} // namespace android +} // namespace events +} // namespace ui diff --git a/chromium/ui/events/android/key_event_utils.h b/chromium/ui/events/android/key_event_utils.h new file mode 100644 index 00000000000..83445d1cd27 --- /dev/null +++ b/chromium/ui/events/android/key_event_utils.h @@ -0,0 +1,31 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_ANDROID_KEY_EVENT_UTILS_H_ +#define UI_EVENTS_ANDROID_KEY_EVENT_UTILS_H_ + +#include <jni.h> + +#include "base/android/scoped_java_ref.h" +#include "ui/events/events_export.h" + +namespace ui { +namespace events { +namespace android { + +bool RegisterKeyEvent(JNIEnv* env); + +EVENTS_EXPORT base::android::ScopedJavaLocalRef<jobject> +CreateKeyEvent(JNIEnv* env, int action, int key_code); + +EVENTS_EXPORT int GetKeyEventUnicodeChar( + JNIEnv* env, + const base::android::JavaRef<jobject>& event, + int meta_state); + +} // namespace android +} // namespace events +} // namespace ui + +#endif // UI_EVENTS_ANDROID_KEY_EVENT_UTISL_H_ diff --git a/chromium/ui/events/blink/DEPS b/chromium/ui/events/blink/DEPS index 6fa4026ac43..9a2a9a8a2e8 100644 --- a/chromium/ui/events/blink/DEPS +++ b/chromium/ui/events/blink/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+cc/input/input_handler.h", + "+cc/input/main_thread_scrolling_reason.h", "+cc/input/scroll_elasticity_helper.h", "+cc/trees/swap_promise_monitor.h", diff --git a/chromium/ui/events/blink/blink_event_util.cc b/chromium/ui/events/blink/blink_event_util.cc index 108013610d7..687f21fa15c 100644 --- a/chromium/ui/events/blink/blink_event_util.cc +++ b/chromium/ui/events/blink/blink_event_util.cc @@ -23,6 +23,7 @@ using blink::WebGestureEvent; using blink::WebInputEvent; +using blink::WebPointerProperties; using blink::WebTouchEvent; using blink::WebTouchPoint; @@ -81,30 +82,11 @@ WebTouchPoint::State ToWebTouchPointState(const MotionEvent& event, return WebTouchPoint::StateUndefined; } -WebTouchPoint::PointerType ToWebTouchPointPointerType(const MotionEvent& event, - size_t pointer_index) { - switch (event.GetToolType(pointer_index)) { - case MotionEvent::TOOL_TYPE_UNKNOWN: - return WebTouchPoint::PointerType::Unknown; - case MotionEvent::TOOL_TYPE_FINGER: - return WebTouchPoint::PointerType::Touch; - case MotionEvent::TOOL_TYPE_STYLUS: - return WebTouchPoint::PointerType::Pen; - case MotionEvent::TOOL_TYPE_MOUSE: - return WebTouchPoint::PointerType::Mouse; - case MotionEvent::TOOL_TYPE_ERASER: - return WebTouchPoint::PointerType::Unknown; - } - NOTREACHED() << "Invalid MotionEvent::ToolType = " - << event.GetToolType(pointer_index); - return WebTouchPoint::PointerType::Unknown; -} - WebTouchPoint CreateWebTouchPoint(const MotionEvent& event, size_t pointer_index) { WebTouchPoint touch; touch.id = event.GetPointerId(pointer_index); - touch.pointerType = ToWebTouchPointPointerType(event, pointer_index); + touch.pointerType = ToWebPointerType(event.GetToolType(pointer_index)); touch.state = ToWebTouchPointState(event, pointer_index); touch.position.x = event.GetX(pointer_index); touch.position.y = event.GetY(pointer_index); @@ -179,7 +161,7 @@ WebTouchPoint CreateWebTouchPoint(const MotionEvent& event, blink::WebTouchEvent CreateWebTouchEventFromMotionEvent( const MotionEvent& event, - bool may_cause_scrolling) { + bool moved_beyond_slop_region) { static_assert(static_cast<int>(MotionEvent::MAX_TOUCH_POINT_COUNT) == static_cast<int>(blink::WebTouchEvent::touchesLengthCap), "inconsistent maximum number of active touch points"); @@ -187,10 +169,12 @@ blink::WebTouchEvent CreateWebTouchEventFromMotionEvent( blink::WebTouchEvent result; result.type = ToWebInputEventType(event.GetAction()); - result.cancelable = (result.type != WebInputEvent::TouchCancel); + result.dispatchType = result.type == WebInputEvent::TouchCancel + ? WebInputEvent::EventNonBlocking + : WebInputEvent::Blocking; result.timeStampSeconds = - (event.GetEventTime() - base::TimeTicks()).InSecondsF(), - result.causesScrollingIfUncanceled = may_cause_scrolling; + (event.GetEventTime() - base::TimeTicks()).InSecondsF(); + result.movedBeyondSlopRegion = moved_beyond_slop_region; result.modifiers = EventFlagsToWebEventModifiers(event.GetFlags()); DCHECK_NE(event.GetUniqueEventId(), 0U); result.uniqueTouchEventId = event.GetUniqueEventId(); @@ -215,16 +199,7 @@ int EventFlagsToWebEventModifiers(int flags) { if (flags & EF_ALT_DOWN) modifiers |= blink::WebInputEvent::AltKey; if (flags & EF_COMMAND_DOWN) -#if defined(OS_WIN) - // Evaluate whether OSKey should be set for other platforms. - // Since this value was never set on Windows before as the meta - // key; we don't break backwards compatiblity exposing it as the - // true OS key. However this is not the case for Linux; see - // http://crbug.com/539979 - modifiers |= blink::WebInputEvent::OSKey; -#else modifiers |= blink::WebInputEvent::MetaKey; -#endif if (flags & EF_ALTGR_DOWN) modifiers |= blink::WebInputEvent::AltGrKey; if (flags & EF_NUM_LOCK_ON) @@ -366,4 +341,127 @@ WebGestureEvent CreateWebGestureEventFromGestureEventData( gfx::PointF(data.raw_x, data.raw_y), data.flags); } +scoped_ptr<blink::WebInputEvent> ScaleWebInputEvent( + const blink::WebInputEvent& event, + float scale) { + scoped_ptr<blink::WebInputEvent> scaled_event; + if (scale == 1.f) + return scaled_event; + if (event.type == blink::WebMouseEvent::MouseWheel) { + blink::WebMouseWheelEvent* wheel_event = new blink::WebMouseWheelEvent; + scaled_event.reset(wheel_event); + *wheel_event = static_cast<const blink::WebMouseWheelEvent&>(event); + wheel_event->x *= scale; + wheel_event->y *= scale; + wheel_event->deltaX *= scale; + wheel_event->deltaY *= scale; + wheel_event->wheelTicksX *= scale; + wheel_event->wheelTicksY *= scale; + } else if (blink::WebInputEvent::isMouseEventType(event.type)) { + blink::WebMouseEvent* mouse_event = new blink::WebMouseEvent; + scaled_event.reset(mouse_event); + *mouse_event = static_cast<const blink::WebMouseEvent&>(event); + mouse_event->x *= scale; + mouse_event->y *= scale; + mouse_event->windowX = mouse_event->x; + mouse_event->windowY = mouse_event->y; + mouse_event->movementX *= scale; + mouse_event->movementY *= scale; + } else if (blink::WebInputEvent::isTouchEventType(event.type)) { + blink::WebTouchEvent* touch_event = new blink::WebTouchEvent; + scaled_event.reset(touch_event); + *touch_event = static_cast<const blink::WebTouchEvent&>(event); + for (unsigned i = 0; i < touch_event->touchesLength; i++) { + touch_event->touches[i].position.x *= scale; + touch_event->touches[i].position.y *= scale; + touch_event->touches[i].radiusX *= scale; + touch_event->touches[i].radiusY *= scale; + } + } else if (blink::WebInputEvent::isGestureEventType(event.type)) { + blink::WebGestureEvent* gesture_event = new blink::WebGestureEvent; + scaled_event.reset(gesture_event); + *gesture_event = static_cast<const blink::WebGestureEvent&>(event); + gesture_event->x *= scale; + gesture_event->y *= scale; + switch (gesture_event->type) { + case blink::WebInputEvent::GestureScrollUpdate: + gesture_event->data.scrollUpdate.deltaX *= scale; + gesture_event->data.scrollUpdate.deltaY *= scale; + break; + case blink::WebInputEvent::GestureScrollBegin: + gesture_event->data.scrollBegin.deltaXHint *= scale; + gesture_event->data.scrollBegin.deltaYHint *= scale; + break; + + case blink::WebInputEvent::GesturePinchUpdate: + // Scale in pinch gesture is DSF agnostic. + break; + + case blink::WebInputEvent::GestureDoubleTap: + case blink::WebInputEvent::GestureTap: + case blink::WebInputEvent::GestureTapUnconfirmed: + gesture_event->data.tap.width *= scale; + gesture_event->data.tap.height *= scale; + break; + + case blink::WebInputEvent::GestureTapDown: + gesture_event->data.tapDown.width *= scale; + gesture_event->data.tapDown.height *= scale; + break; + + case blink::WebInputEvent::GestureShowPress: + gesture_event->data.showPress.width *= scale; + gesture_event->data.showPress.height *= scale; + break; + + case blink::WebInputEvent::GestureLongPress: + case blink::WebInputEvent::GestureLongTap: + gesture_event->data.longPress.width *= scale; + gesture_event->data.longPress.height *= scale; + break; + + case blink::WebInputEvent::GestureTwoFingerTap: + gesture_event->data.twoFingerTap.firstFingerWidth *= scale; + gesture_event->data.twoFingerTap.firstFingerHeight *= scale; + break; + + case blink::WebInputEvent::GestureFlingStart: + gesture_event->data.flingStart.velocityX *= scale; + gesture_event->data.flingStart.velocityY *= scale; + break; + + // These event does not have location data. + case blink::WebInputEvent::GesturePinchBegin: + case blink::WebInputEvent::GesturePinchEnd: + case blink::WebInputEvent::GestureTapCancel: + case blink::WebInputEvent::GestureFlingCancel: + case blink::WebInputEvent::GestureScrollEnd: + break; + + // TODO(oshima): Find out if ContextMenu needs to be scaled. + default: + break; + } + } + return scaled_event; +} + +WebPointerProperties::PointerType ToWebPointerType( + MotionEvent::ToolType tool_type) { + switch (tool_type) { + case MotionEvent::TOOL_TYPE_UNKNOWN: + return WebPointerProperties::PointerType::Unknown; + case MotionEvent::TOOL_TYPE_FINGER: + return WebPointerProperties::PointerType::Touch; + case MotionEvent::TOOL_TYPE_STYLUS: + return WebPointerProperties::PointerType::Pen; + case MotionEvent::TOOL_TYPE_MOUSE: + return WebPointerProperties::PointerType::Mouse; + case MotionEvent::TOOL_TYPE_ERASER: + return WebPointerProperties::PointerType::Unknown; + } + NOTREACHED() << "Invalid MotionEvent::ToolType = " << tool_type; + return WebPointerProperties::PointerType::Unknown; +} + } // namespace ui diff --git a/chromium/ui/events/blink/blink_event_util.h b/chromium/ui/events/blink/blink_event_util.h index c0f60c921ec..bee5c7a6038 100644 --- a/chromium/ui/events/blink/blink_event_util.h +++ b/chromium/ui/events/blink/blink_event_util.h @@ -5,12 +5,17 @@ #ifndef UI_EVENTS_BLINK_BLINK_EVENT_UTIL_H_ #define UI_EVENTS_BLINK_BLINK_EVENT_UTIL_H_ +#include "base/memory/scoped_ptr.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/gesture_detection/motion_event.h" + namespace base { class TimeDelta; } namespace blink { class WebGestureEvent; +class WebInputEvent; class WebTouchEvent; } @@ -39,6 +44,13 @@ blink::WebGestureEvent CreateWebGestureEventFromGestureEventData( int EventFlagsToWebEventModifiers(int flags); -} // namespace content +scoped_ptr<blink::WebInputEvent> ScaleWebInputEvent( + const blink::WebInputEvent& event, + float scale); + +blink::WebPointerProperties::PointerType ToWebPointerType( + MotionEvent::ToolType tool_type); + +} // namespace ui #endif // UI_EVENTS_BLINK_BLINK_EVENT_UTIL_H_ diff --git a/chromium/ui/events/blink/blink_event_util_unittest.cc b/chromium/ui/events/blink/blink_event_util_unittest.cc new file mode 100644 index 00000000000..cb0830caabe --- /dev/null +++ b/chromium/ui/events/blink/blink_event_util_unittest.cc @@ -0,0 +1,30 @@ +// 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/events/blink/blink_event_util.h" + +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/gesture_event_details.h" + +namespace ui { + +using BlinkEventUtilTest = testing::Test; + +TEST(BlinkEventUtilTest, NoScalingWith1DSF) { + const ui::GestureEventDetails details(ui::ET_GESTURE_SCROLL_UPDATE, + 1, + 1); + auto event = + CreateWebGestureEvent(details, + base::TimeDelta::FromMilliseconds(1), + gfx::PointF(1.f, 1.f), + gfx::PointF(1.f, 1.f), + 0); + EXPECT_FALSE(ScaleWebInputEvent(event, 1.f)); + EXPECT_TRUE(ScaleWebInputEvent(event, 2.f)); +} + +} // namespace ui diff --git a/chromium/ui/events/blink/input_handler_proxy.cc b/chromium/ui/events/blink/input_handler_proxy.cc index 0c164d704ff..2ac8c61b9e9 100644 --- a/chromium/ui/events/blink/input_handler_proxy.cc +++ b/chromium/ui/events/blink/input_handler_proxy.cc @@ -16,6 +16,7 @@ #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" +#include "cc/input/main_thread_scrolling_reason.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/events/blink/input_handler_proxy_client.h" #include "ui/events/blink/input_scroll_elasticity_controller.h" @@ -34,6 +35,8 @@ using blink::WebTouchPoint; namespace { +const int32_t kEventDispositionUndefined = -1; + // Maximum time between a fling event's timestamp and the first |Animate| call // for the fling curve to use the fling timestamp as the initial animation time. // Two frames allows a minor delay between event creation and the first animate. @@ -122,28 +125,34 @@ WebGestureEvent ObtainGestureScrollBegin(const WebGestureEvent& event) { } cc::ScrollState CreateScrollStateForGesture(const WebGestureEvent& event) { + cc::ScrollStateData scroll_state_data; switch (event.type) { case WebInputEvent::GestureScrollBegin: - return cc::ScrollState(0, 0, event.x, event.y, 0, 0, true, false, false); + scroll_state_data.start_position_x = event.x; + scroll_state_data.start_position_y = event.y; + scroll_state_data.is_beginning = true; + break; case WebInputEvent::GestureFlingStart: - return cc::ScrollState( - 0, 0, event.x, event.y, event.data.flingStart.velocityX, - event.data.flingStart.velocityX, true, true, false); + scroll_state_data.velocity_x = event.data.flingStart.velocityX; + scroll_state_data.velocity_y = event.data.flingStart.velocityY; + scroll_state_data.is_in_inertial_phase = true; + break; case WebInputEvent::GestureScrollUpdate: - return cc::ScrollState(-event.data.scrollUpdate.deltaX, - -event.data.scrollUpdate.deltaY, event.x, event.y, - event.data.scrollUpdate.velocityX, - event.data.scrollUpdate.velocityY, - event.data.scrollUpdate.inertial, false, false); + scroll_state_data.delta_x = -event.data.scrollUpdate.deltaX; + scroll_state_data.delta_y = -event.data.scrollUpdate.deltaY; + scroll_state_data.velocity_x = event.data.scrollUpdate.velocityX; + scroll_state_data.velocity_y = event.data.scrollUpdate.velocityY; + scroll_state_data.is_in_inertial_phase = event.data.scrollUpdate.inertial; + break; case WebInputEvent::GestureScrollEnd: - return cc::ScrollState(0, 0, event.x, event.y, 0, 0, false, false, true); case WebInputEvent::GestureFlingCancel: - return cc::ScrollState(0, 0, event.x, event.y, 0, 0, false, true, true); - + scroll_state_data.is_ending = true; + break; default: NOTREACHED(); - return cc::ScrollState(0, 0, 0, 0, 0, 0, false, false, false); + break; } + return cc::ScrollState(scroll_state_data); } void ReportInputEventLatencyUma(const WebInputEvent& event, @@ -199,6 +208,13 @@ void ReportInputEventLatencyUma(const WebInputEvent& event, } } +cc::InputHandler::ScrollInputType GestureScrollInputType( + blink::WebGestureDevice device) { + return device == blink::WebGestureDeviceTouchpad + ? cc::InputHandler::WHEEL + : cc::InputHandler::TOUCHSCREEN; +} + } // namespace namespace ui { @@ -220,7 +236,9 @@ InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler, disallow_vertical_fling_scroll_(false), has_fling_animation_started_(false), smooth_scroll_enabled_(false), - uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()) { + uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()), + use_gesture_events_for_mouse_wheel_(true), + touch_start_result_(kEventDispositionUndefined) { DCHECK(client); input_handler_->BindToClient(this); cc::ScrollElasticityHelper* scroll_elasticity_helper = @@ -287,8 +305,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( const WebGestureEvent& gesture_event = static_cast<const WebGestureEvent&>(event); if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad && - input_handler_->HaveWheelEventHandlersAt( - gfx::Point(gesture_event.x, gesture_event.y))) { + input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kMouseWheel) != + cc::EventListenerProperties::kNone) { return DID_NOT_HANDLE; } else { input_handler_->PinchGestureBegin(); @@ -335,6 +354,12 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( case WebInputEvent::TouchStart: return HandleTouchStart(static_cast<const WebTouchEvent&>(event)); + case WebInputEvent::TouchMove: + return HandleTouchMove(static_cast<const WebTouchEvent&>(event)); + + case WebInputEvent::TouchEnd: + return HandleTouchEnd(static_cast<const WebTouchEvent&>(event)); + case WebInputEvent::MouseMove: { const WebMouseEvent& mouse_event = static_cast<const WebMouseEvent&>(event); @@ -359,18 +384,92 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( return DID_NOT_HANDLE; } -bool InputHandlerProxy::ShouldAnimate( - const blink::WebMouseWheelEvent& event) const { +void InputHandlerProxy::RecordMainThreadScrollingReasons( + WebInputEvent::Type type, + uint32_t reasons) { + static const char* kGestureHistogramName = + "Renderer4.MainThreadGestureScrollReason"; + static const char* kWheelHistogramName = + "Renderer4.MainThreadWheelScrollReason"; + + DCHECK(type == WebInputEvent::GestureScrollBegin || + type == WebInputEvent::MouseWheel); + + if (type != WebInputEvent::GestureScrollBegin && + type != WebInputEvent::MouseWheel) { + return; + } + + if (reasons == cc::MainThreadScrollingReason::kNotScrollingOnMain) { + if (type == WebInputEvent::GestureScrollBegin) { + UMA_HISTOGRAM_ENUMERATION( + kGestureHistogramName, + cc::MainThreadScrollingReason::kNotScrollingOnMain, + cc::MainThreadScrollingReason::kMainThreadScrollingReasonCount); + } else { + UMA_HISTOGRAM_ENUMERATION( + kWheelHistogramName, + cc::MainThreadScrollingReason::kNotScrollingOnMain, + cc::MainThreadScrollingReason::kMainThreadScrollingReasonCount); + } + } + + for (uint32_t i = 0; + i < cc::MainThreadScrollingReason::kMainThreadScrollingReasonCount - 1; + ++i) { + unsigned val = 1 << i; + if (reasons & val) { + if (type == WebInputEvent::GestureScrollBegin) { + UMA_HISTOGRAM_ENUMERATION( + kGestureHistogramName, i + 1, + cc::MainThreadScrollingReason::kMainThreadScrollingReasonCount); + } else { + UMA_HISTOGRAM_ENUMERATION( + kWheelHistogramName, i + 1, + cc::MainThreadScrollingReason::kMainThreadScrollingReasonCount); + } + } + } +} + +bool InputHandlerProxy::ShouldAnimate(bool has_precise_scroll_deltas) const { #if defined(OS_MACOSX) // Mac does not smooth scroll wheel events (crbug.com/574283). return false; #else - return smooth_scroll_enabled_ && !event.hasPreciseScrollingDeltas; + return smooth_scroll_enabled_ && !has_precise_scroll_deltas; #endif } InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel( const WebMouseWheelEvent& wheel_event) { + // Only call |CancelCurrentFling()| if a fling was active, as it will + // otherwise disrupt an in-progress touch scroll. + if (!wheel_event.hasPreciseScrollingDeltas && fling_curve_) + CancelCurrentFling(); + + if (use_gesture_events_for_mouse_wheel_) { + cc::EventListenerProperties properties = + input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kMouseWheel); + switch (properties) { + case cc::EventListenerProperties::kPassive: + return DID_HANDLE_NON_BLOCKING; + case cc::EventListenerProperties::kBlockingAndPassive: + case cc::EventListenerProperties::kBlocking: + return DID_NOT_HANDLE; + case cc::EventListenerProperties::kNone: + return DROP_EVENT; + default: + NOTREACHED(); + return DROP_EVENT; + } + } + return ScrollByMouseWheel(wheel_event); +} + +InputHandlerProxy::EventDisposition InputHandlerProxy::ScrollByMouseWheel( + const WebMouseWheelEvent& wheel_event) { InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE; cc::InputHandlerScrollResult scroll_result; @@ -388,16 +487,23 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel( // TODO(jamesr): We don't properly handle scroll by page in the compositor // thread, so punt it to the main thread. http://crbug.com/236639 result = DID_NOT_HANDLE; + RecordMainThreadScrollingReasons( + wheel_event.type, cc::MainThreadScrollingReason::kPageBasedScrolling); + } else if (!wheel_event.canScroll) { // Wheel events with |canScroll| == false will not trigger scrolling, // only event handlers. Forward to the main thread. result = DID_NOT_HANDLE; - } else if (ShouldAnimate(wheel_event)) { + } else if (ShouldAnimate(wheel_event.hasPreciseScrollingDeltas)) { cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollAnimated(gfx::Point(wheel_event.x, wheel_event.y), scroll_delta); - switch (scroll_status) { - case cc::InputHandler::SCROLL_STARTED: + + RecordMainThreadScrollingReasons( + wheel_event.type, scroll_status.main_thread_scrolling_reasons); + + switch (scroll_status.thread) { + case cc::InputHandler::SCROLL_ON_IMPL_THREAD: result = DID_HANDLE; break; case cc::InputHandler::SCROLL_IGNORED: @@ -408,27 +514,38 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel( break; } } else { - cc::ScrollState scroll_state_begin(0, 0, wheel_event.x, wheel_event.y, 0, 0, - true, false, false); - cc::InputHandler::ScrollStatus scroll_status = - input_handler_->ScrollBegin(&scroll_state_begin, - cc::InputHandler::WHEEL); - switch (scroll_status) { - case cc::InputHandler::SCROLL_STARTED: { + cc::ScrollStateData scroll_state_begin_data; + scroll_state_begin_data.start_position_x = wheel_event.x; + scroll_state_begin_data.start_position_y = wheel_event.y; + scroll_state_begin_data.is_beginning = true; + cc::ScrollState scroll_state_begin(scroll_state_begin_data); + cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin( + &scroll_state_begin, cc::InputHandler::WHEEL); + + RecordMainThreadScrollingReasons( + wheel_event.type, scroll_status.main_thread_scrolling_reasons); + + switch (scroll_status.thread) { + case cc::InputHandler::SCROLL_ON_IMPL_THREAD: { TRACE_EVENT_INSTANT2("input", "InputHandlerProxy::handle_input wheel scroll", TRACE_EVENT_SCOPE_THREAD, "deltaX", scroll_delta.x(), "deltaY", scroll_delta.y()); - cc::ScrollState scroll_state_update(scroll_delta.x(), scroll_delta.y(), - wheel_event.x, wheel_event.y, 0, 0, - false, false, false); + + cc::ScrollStateData scroll_state_update_data; + scroll_state_update_data.delta_x = scroll_delta.x(); + scroll_state_update_data.delta_y = scroll_delta.y(); + scroll_state_update_data.start_position_x = wheel_event.x; + scroll_state_update_data.start_position_y = wheel_event.y; + cc::ScrollState scroll_state_update(scroll_state_update_data); scroll_result = input_handler_->ScrollBy(&scroll_state_update); HandleOverscroll(gfx::Point(wheel_event.x, wheel_event.y), scroll_result); - cc::ScrollState scroll_state_end(0, 0, wheel_event.x, wheel_event.y, - 0, 0, false, false, true); + cc::ScrollStateData scroll_state_end_data; + scroll_state_end_data.is_ending = true; + cc::ScrollState scroll_state_end(scroll_state_end_data); input_handler_->ScrollEnd(&scroll_state_end); result = scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT; @@ -445,9 +562,6 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel( case cc::InputHandler::SCROLL_ON_MAIN_THREAD: result = DID_NOT_HANDLE; break; - case cc::InputHandler::ScrollStatusCount: - NOTREACHED(); - break; } } @@ -479,33 +593,51 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin( #endif cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); cc::InputHandler::ScrollStatus scroll_status; - if (gesture_event.data.scrollBegin.targetViewport) { - scroll_status = input_handler_->RootScrollBegin(&scroll_state, - cc::InputHandler::GESTURE); + if (gesture_event.data.scrollBegin.deltaHintUnits == + blink::WebGestureEvent::ScrollUnits::Page) { + scroll_status.thread = cc::InputHandler::SCROLL_ON_MAIN_THREAD; + scroll_status.main_thread_scrolling_reasons = + cc::MainThreadScrollingReason::kContinuingMainThreadScroll; + } else if (gesture_event.data.scrollBegin.targetViewport) { + scroll_status = input_handler_->RootScrollBegin( + &scroll_state, GestureScrollInputType(gesture_event.sourceDevice)); + } else if (ShouldAnimate(gesture_event.data.scrollBegin.deltaHintUnits != + blink::WebGestureEvent::ScrollUnits::Pixels)) { + gfx::Point scroll_point(gesture_event.x, gesture_event.y); + scroll_status = input_handler_->ScrollAnimatedBegin(scroll_point); } else { - scroll_status = - input_handler_->ScrollBegin(&scroll_state, cc::InputHandler::GESTURE); + scroll_status = input_handler_->ScrollBegin( + &scroll_state, GestureScrollInputType(gesture_event.sourceDevice)); } UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult", - scroll_status, - cc::InputHandler::ScrollStatusCount); - switch (scroll_status) { - case cc::InputHandler::SCROLL_STARTED: + scroll_status.thread, + cc::InputHandler::LAST_SCROLL_STATUS + 1); + + RecordMainThreadScrollingReasons(gesture_event.type, + scroll_status.main_thread_scrolling_reasons); + + InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE; + switch (scroll_status.thread) { + case cc::InputHandler::SCROLL_ON_IMPL_THREAD: TRACE_EVENT_INSTANT0("input", "InputHandlerProxy::handle_input gesture scroll", TRACE_EVENT_SCOPE_THREAD); gesture_scroll_on_impl_thread_ = true; - return DID_HANDLE; + result = DID_HANDLE; + break; case cc::InputHandler::SCROLL_UNKNOWN: case cc::InputHandler::SCROLL_ON_MAIN_THREAD: - return DID_NOT_HANDLE; + result = DID_NOT_HANDLE; + break; case cc::InputHandler::SCROLL_IGNORED: - return DROP_EVENT; - case cc::InputHandler::ScrollStatusCount: - NOTREACHED(); + result = DROP_EVENT; break; } - return DID_NOT_HANDLE; + if (scroll_elasticity_controller_ && result != DID_NOT_HANDLE) + HandleScrollElasticityOverscroll(gesture_event, + cc::InputHandlerScrollResult()); + + return result; } InputHandlerProxy::EventDisposition @@ -514,14 +646,32 @@ InputHandlerProxy::HandleGestureScrollUpdate( #ifndef NDEBUG DCHECK(expect_scroll_update_end_); #endif - if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_) return DID_NOT_HANDLE; cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); + gfx::Point scroll_point(gesture_event.x, gesture_event.y); + gfx::Vector2dF scroll_delta(-gesture_event.data.scrollUpdate.deltaX, + -gesture_event.data.scrollUpdate.deltaY); + + if (ShouldAnimate(gesture_event.data.scrollUpdate.deltaUnits != + blink::WebGestureEvent::ScrollUnits::Pixels)) { + switch (input_handler_->ScrollAnimated(scroll_point, scroll_delta).thread) { + case cc::InputHandler::SCROLL_ON_IMPL_THREAD: + return DID_HANDLE; + case cc::InputHandler::SCROLL_IGNORED: + return DROP_EVENT; + default: + return DID_NOT_HANDLE; + } + } cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(&scroll_state); - HandleOverscroll(gfx::Point(gesture_event.x, gesture_event.y), scroll_result); + HandleOverscroll(scroll_point, scroll_result); + + if (scroll_elasticity_controller_) + HandleScrollElasticityOverscroll(gesture_event, scroll_result); + return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT; } @@ -531,10 +681,21 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd( DCHECK(expect_scroll_update_end_); expect_scroll_update_end_ = false; #endif - cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); - input_handler_->ScrollEnd(&scroll_state); + if (ShouldAnimate(gesture_event.data.scrollEnd.deltaUnits != + blink::WebGestureEvent::ScrollUnits::Pixels)) { + // Do nothing if the scroll is being animated; the scroll animation will + // generate the ScrollEnd when it is done. + } else { + cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); + input_handler_->ScrollEnd(&scroll_state); + } if (!gesture_scroll_on_impl_thread_) return DID_NOT_HANDLE; + + if (scroll_elasticity_controller_) + HandleScrollElasticityOverscroll(gesture_event, + cc::InputHandlerScrollResult()); + gesture_scroll_on_impl_thread_ = false; return DID_HANDLE; } @@ -542,8 +703,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd( InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( const WebGestureEvent& gesture_event) { cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); - cc::InputHandler::ScrollStatus scroll_status = - cc::InputHandler::SCROLL_ON_MAIN_THREAD; + cc::InputHandler::ScrollStatus scroll_status; + scroll_status.main_thread_scrolling_reasons = + cc::MainThreadScrollingReason::kNotScrollingOnMain; switch (gesture_event.sourceDevice) { case blink::WebGestureDeviceTouchpad: if (gesture_event.data.flingStart.targetViewport) { @@ -555,10 +717,13 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( } break; case blink::WebGestureDeviceTouchscreen: - if (!gesture_scroll_on_impl_thread_) - scroll_status = cc::InputHandler::SCROLL_ON_MAIN_THREAD; - else + if (!gesture_scroll_on_impl_thread_) { + scroll_status.thread = cc::InputHandler::SCROLL_ON_MAIN_THREAD; + scroll_status.main_thread_scrolling_reasons = + cc::MainThreadScrollingReason::kContinuingMainThreadScroll; + } else { scroll_status = input_handler_->FlingScrollBegin(); + } break; case blink::WebGestureDeviceUninitialized: NOTREACHED(); @@ -569,8 +734,8 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( expect_scroll_update_end_ = false; #endif - switch (scroll_status) { - case cc::InputHandler::SCROLL_STARTED: { + switch (scroll_status.thread) { + case cc::InputHandler::SCROLL_ON_IMPL_THREAD: { if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) { scroll_state.set_is_ending(true); input_handler_->ScrollEnd(&scroll_state); @@ -626,27 +791,84 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( } return DROP_EVENT; } - case cc::InputHandler::ScrollStatusCount: - NOTREACHED(); - break; } return DID_NOT_HANDLE; } InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart( const blink::WebTouchEvent& touch_event) { + EventDisposition result = DROP_EVENT; for (size_t i = 0; i < touch_event.touchesLength; ++i) { if (touch_event.touches[i].state != WebTouchPoint::StatePressed) continue; if (input_handler_->DoTouchEventsBlockScrollAt( gfx::Point(touch_event.touches[i].position.x, touch_event.touches[i].position.y))) { - // TODO(rbyers): We should consider still sending the touch events to - // main asynchronously (crbug.com/455539). - return DID_NOT_HANDLE; + result = DID_NOT_HANDLE; + break; } } - return DROP_EVENT; + + // If |result| is DROP_EVENT it wasn't processed above. + if (result == DROP_EVENT) { + switch (input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kTouchStartOrMove)) { + case cc::EventListenerProperties::kPassive: + result = DID_HANDLE_NON_BLOCKING; + break; + case cc::EventListenerProperties::kBlocking: + // The touch area rects above already have checked whether it hits + // a blocking region. Since it does not the event can be dropped. + result = DROP_EVENT; + break; + case cc::EventListenerProperties::kBlockingAndPassive: + // There is at least one passive listener that needs to possibly + // be notified so it can't be dropped. + result = DID_HANDLE_NON_BLOCKING; + break; + case cc::EventListenerProperties::kNone: + result = DROP_EVENT; + break; + default: + NOTREACHED(); + result = DROP_EVENT; + break; + } + } + + // Merge |touch_start_result_| and |result| so the result has the highest + // priority value according to the sequence; (DROP_EVENT, + // DID_HANDLE_NON_BLOCKING, DID_NOT_HANDLE). + if (touch_start_result_ == kEventDispositionUndefined || + touch_start_result_ == DROP_EVENT || result == DID_NOT_HANDLE) + touch_start_result_ = result; + + // 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 + // code is explicitly after the assignment of the |touch_start_result_| + // so the touch moves are not sent to the main thread un-necessarily. + if (result == DROP_EVENT && + input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kTouchEndOrCancel) != + cc::EventListenerProperties::kNone) { + result = DID_HANDLE_NON_BLOCKING; + } + + return result; +} + +InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchMove( + const blink::WebTouchEvent& touch_event) { + if (touch_start_result_ != kEventDispositionUndefined) + return static_cast<EventDisposition>(touch_start_result_); + return DID_NOT_HANDLE; +} + +InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchEnd( + const blink::WebTouchEvent& touch_event) { + if (touch_event.touchesLength == 1) + touch_start_result_ = kEventDispositionUndefined; + return DID_NOT_HANDLE; } bool InputHandlerProxy::FilterInputEventForFlingBoosting( @@ -697,7 +919,7 @@ bool InputHandlerProxy::FilterInputEventForFlingBoosting( gfx::Point(gesture_event.x, gesture_event.y), fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad ? cc::InputHandler::NON_BUBBLING_GESTURE - : cc::InputHandler::GESTURE)) { + : cc::InputHandler::TOUCHSCREEN)) { CancelCurrentFling(); return false; } @@ -899,6 +1121,14 @@ void InputHandlerProxy::SynchronouslySetRootScrollOffset( input_handler_->SetSynchronousInputHandlerRootScrollOffset(root_offset); } +void InputHandlerProxy::SynchronouslyZoomBy(float magnify_delta, + const gfx::Point& anchor) { + DCHECK(synchronous_input_handler_); + input_handler_->PinchGestureBegin(); + input_handler_->PinchGestureUpdate(magnify_delta, anchor); + input_handler_->PinchGestureEnd(); +} + void InputHandlerProxy::HandleOverscroll( const gfx::Point& causal_event_viewport_point, const cc::InputHandlerScrollResult& scroll_result) { @@ -938,12 +1168,12 @@ bool InputHandlerProxy::CancelCurrentFling() { } bool InputHandlerProxy::CancelCurrentFlingWithoutNotifyingClient() { - bool had_fling_animation = fling_curve_; + bool had_fling_animation = !!fling_curve_; if (had_fling_animation && fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchscreen) { - cc::ScrollState scroll_state(0, 0, fling_parameters_.point.x, - fling_parameters_.point.y, 0, 0, false, true, - true); + cc::ScrollStateData scroll_state_data; + scroll_state_data.is_ending = true; + cc::ScrollState scroll_state(scroll_state_data); input_handler_->ScrollEnd(&scroll_state); TRACE_EVENT_ASYNC_END0( "input", @@ -990,25 +1220,47 @@ void InputHandlerProxy::RequestAnimation() { bool InputHandlerProxy::TouchpadFlingScroll( const WebFloatSize& increment) { - WebMouseWheelEvent synthetic_wheel; - synthetic_wheel.type = WebInputEvent::MouseWheel; - synthetic_wheel.timeStampSeconds = InSecondsF(base::TimeTicks::Now()); - synthetic_wheel.deltaX = increment.width; - synthetic_wheel.deltaY = increment.height; - synthetic_wheel.hasPreciseScrollingDeltas = true; - synthetic_wheel.x = fling_parameters_.point.x; - synthetic_wheel.y = fling_parameters_.point.y; - synthetic_wheel.globalX = fling_parameters_.globalPoint.x; - synthetic_wheel.globalY = fling_parameters_.globalPoint.y; - synthetic_wheel.modifiers = fling_parameters_.modifiers; - - InputHandlerProxy::EventDisposition disposition = - HandleInputEvent(synthetic_wheel); + InputHandlerProxy::EventDisposition disposition; + cc::EventListenerProperties properties = + input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kMouseWheel); + switch (properties) { + case cc::EventListenerProperties::kPassive: + disposition = DID_HANDLE_NON_BLOCKING; + break; + case cc::EventListenerProperties::kBlocking: + disposition = DID_NOT_HANDLE; + break; + case cc::EventListenerProperties::kNone: { + WebMouseWheelEvent synthetic_wheel; + synthetic_wheel.type = WebInputEvent::MouseWheel; + synthetic_wheel.timeStampSeconds = InSecondsF(base::TimeTicks::Now()); + synthetic_wheel.deltaX = increment.width; + synthetic_wheel.deltaY = increment.height; + synthetic_wheel.hasPreciseScrollingDeltas = true; + synthetic_wheel.x = fling_parameters_.point.x; + synthetic_wheel.y = fling_parameters_.point.y; + synthetic_wheel.globalX = fling_parameters_.globalPoint.x; + synthetic_wheel.globalY = fling_parameters_.globalPoint.y; + synthetic_wheel.modifiers = fling_parameters_.modifiers; + + disposition = ScrollByMouseWheel(synthetic_wheel); + break; + } + default: + NOTREACHED(); + return false; + } + switch (disposition) { case DID_HANDLE: return true; case DROP_EVENT: break; + case DID_HANDLE_NON_BLOCKING: + // TODO(dtapuska): Process the fling on the compositor thread + // but post the events to the main thread; for now just pass it to the + // main thread. case DID_NOT_HANDLE: TRACE_EVENT_INSTANT0("input", "InputHandlerProxy::scrollBy::AbortFling", @@ -1063,10 +1315,13 @@ bool InputHandlerProxy::scrollBy(const WebFloatSize& increment, break; case blink::WebGestureDeviceTouchscreen: { clipped_increment = ToClientScrollIncrement(clipped_increment); - cc::ScrollState scroll_state( - clipped_increment.width, clipped_increment.height, - fling_parameters_.point.x, fling_parameters_.point.y, - clipped_velocity.width, clipped_velocity.height, false, true, false); + cc::ScrollStateData scroll_state_data; + scroll_state_data.delta_x = clipped_increment.width; + scroll_state_data.delta_y = clipped_increment.height; + scroll_state_data.velocity_x = clipped_velocity.width; + scroll_state_data.velocity_y = clipped_velocity.height; + scroll_state_data.is_in_inertial_phase = true; + cc::ScrollState scroll_state(scroll_state_data); cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(&scroll_state); HandleOverscroll(fling_parameters_.point, scroll_result); @@ -1092,4 +1347,19 @@ bool InputHandlerProxy::scrollBy(const WebFloatSize& increment, return did_scroll; } +void InputHandlerProxy::HandleScrollElasticityOverscroll( + const WebGestureEvent& gesture_event, + const cc::InputHandlerScrollResult& scroll_result) { + DCHECK(scroll_elasticity_controller_); + // Send the event and its disposition to the elasticity controller to update + // the over-scroll animation. Note that the call to the elasticity controller + // is made asynchronously, to minimize divergence between main thread and + // impl thread event handling paths. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&InputScrollElasticityController::ObserveGestureEventAndResult, + scroll_elasticity_controller_->GetWeakPtr(), gesture_event, + scroll_result)); +} + } // namespace ui diff --git a/chromium/ui/events/blink/input_handler_proxy.h b/chromium/ui/events/blink/input_handler_proxy.h index e96b31bf40b..4ef107fba76 100644 --- a/chromium/ui/events/blink/input_handler_proxy.h +++ b/chromium/ui/events/blink/input_handler_proxy.h @@ -46,10 +46,14 @@ class InputHandlerProxy } void set_smooth_scroll_enabled(bool value) { smooth_scroll_enabled_ = value; } + void set_use_gesture_events_for_mouse_wheel(bool value) { + use_gesture_events_for_mouse_wheel_ = value; + } enum EventDisposition { DID_HANDLE, DID_NOT_HANDLE, + DID_HANDLE_NON_BLOCKING, DROP_EVENT }; EventDisposition HandleInputEventWithLatencyInfo( @@ -76,6 +80,8 @@ class InputHandlerProxy void SynchronouslyAnimate(base::TimeTicks time) override; void SynchronouslySetRootScrollOffset( const gfx::ScrollOffset& root_offset) override; + void SynchronouslyZoomBy(float magnify_delta, + const gfx::Point& anchor) override; // blink::WebGestureCurveTarget implementation. bool scrollBy(const blink::WebFloatSize& offset, @@ -85,12 +91,17 @@ class InputHandlerProxy return gesture_scroll_on_impl_thread_; } + protected: + void RecordMainThreadScrollingReasons(blink::WebInputEvent::Type type, + uint32_t reasons); + private: friend class test::InputHandlerProxyTest; // Helper functions for handling more complicated input events. EventDisposition HandleMouseWheel( const blink::WebMouseWheelEvent& event); + EventDisposition ScrollByMouseWheel(const blink::WebMouseWheelEvent& event); EventDisposition HandleGestureScrollBegin( const blink::WebGestureEvent& event); EventDisposition HandleGestureScrollUpdate( @@ -99,8 +110,9 @@ class InputHandlerProxy const blink::WebGestureEvent& event); EventDisposition HandleGestureFlingStart( const blink::WebGestureEvent& event); - EventDisposition HandleTouchStart( - const blink::WebTouchEvent& event); + EventDisposition HandleTouchStart(const blink::WebTouchEvent& event); + EventDisposition HandleTouchMove(const blink::WebTouchEvent& event); + EventDisposition HandleTouchEnd(const blink::WebTouchEvent& event); // Returns true if the event should be suppressed due to to an active, // boost-enabled fling, in which case further processing should cease. @@ -132,7 +144,12 @@ class InputHandlerProxy const cc::InputHandlerScrollResult& scroll_result); // Whether to use a smooth scroll animation for this event. - bool ShouldAnimate(const blink::WebMouseWheelEvent& event) const; + bool ShouldAnimate(bool has_precise_scroll_deltas) const; + + // Update the elastic overscroll controller with |gesture_event|. + void HandleScrollElasticityOverscroll( + const blink::WebGestureEvent& gesture_event, + const cc::InputHandlerScrollResult& scroll_result); scoped_ptr<blink::WebGestureCurve> fling_curve_; // Parameters for the active fling animation, stored in case we need to @@ -184,8 +201,13 @@ class InputHandlerProxy scoped_ptr<InputScrollElasticityController> scroll_elasticity_controller_; bool smooth_scroll_enabled_; - bool uma_latency_reporting_enabled_; + bool use_gesture_events_for_mouse_wheel_; + + // The merged result of the last touch start with previous touch starts. + // This value will get returned for subsequent TouchMove events to allow + // passive events not to block scrolling. + int32_t touch_start_result_; base::TimeTicks last_fling_animate_time_; diff --git a/chromium/ui/events/blink/input_handler_proxy_unittest.cc b/chromium/ui/events/blink/input_handler_proxy_unittest.cc index ccf24f47e6f..1ff2d374e10 100644 --- a/chromium/ui/events/blink/input_handler_proxy_unittest.cc +++ b/chromium/ui/events/blink/input_handler_proxy_unittest.cc @@ -6,6 +6,8 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/test/histogram_tester.h" +#include "cc/input/main_thread_scrolling_reason.h" #include "cc/trees/swap_promise_monitor.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -103,6 +105,8 @@ class MockInputHandler : public cc::InputHandler { MOCK_METHOD2(RootScrollBegin, ScrollStatus(cc::ScrollState*, cc::InputHandler::ScrollInputType type)); + MOCK_METHOD1(ScrollAnimatedBegin, + ScrollStatus(const gfx::Point& viewport_point)); MOCK_METHOD2(ScrollAnimated, ScrollStatus(const gfx::Point& viewport_point, const gfx::Vector2dF& scroll_delta)); @@ -130,7 +134,9 @@ class MockInputHandler : public cc::InputHandler { bool(const gfx::Point& point, cc::InputHandler::ScrollInputType type)); - MOCK_METHOD1(HaveWheelEventHandlersAt, bool(const gfx::Point& point)); + MOCK_CONST_METHOD1( + GetEventListenerProperties, + cc::EventListenerProperties(cc::EventListenerClass event_class)); MOCK_METHOD1(DoTouchEventsBlockScrollAt, bool(const gfx::Point& point)); MOCK_METHOD0(RequestUpdateForSynchronousInputHandler, void()); @@ -242,6 +248,29 @@ WebTouchPoint CreateWebTouchPoint(WebTouchPoint::State state, float x, return point; } +const cc::InputHandler::ScrollStatus kImplThreadScrollState( + cc::InputHandler::SCROLL_ON_IMPL_THREAD, + cc::MainThreadScrollingReason::kNotScrollingOnMain); + +const cc::InputHandler::ScrollStatus kMainThreadScrollState( + cc::InputHandler::SCROLL_ON_MAIN_THREAD, + cc::MainThreadScrollingReason::kEventHandlers); + +const cc::InputHandler::ScrollStatus kScrollIgnoredScrollState( + cc::InputHandler::SCROLL_IGNORED, + cc::MainThreadScrollingReason::kNotScrollable); + +class TestInputHandlerProxy : public InputHandlerProxy { + public: + TestInputHandlerProxy(cc::InputHandler* input_handler, + InputHandlerProxyClient* client) + : InputHandlerProxy(input_handler, client) {} + void RecordMainThreadScrollingReasonsForTest(blink::WebInputEvent::Type type, + uint32_t reasons) { + RecordMainThreadScrollingReasons(type, reasons); + } +}; + } // namespace class InputHandlerProxyTest @@ -255,7 +284,7 @@ class InputHandlerProxyTest GetParam() == CHILD_SCROLL_SYNCHRONOUS_HANDLER), expected_disposition_(InputHandlerProxy::DID_HANDLE) { input_handler_.reset( - new ui::InputHandlerProxy( + new TestInputHandlerProxy( &mock_input_handler_, &mock_client_)); scroll_result_did_scroll_.did_scroll = true; scroll_result_did_not_scroll_.did_scroll = false; @@ -321,7 +350,7 @@ class InputHandlerProxyTest VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = source_device; EXPECT_EQ(expected_disposition_, @@ -330,7 +359,7 @@ class InputHandlerProxyTest VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); gesture_ = @@ -354,22 +383,32 @@ class InputHandlerProxyTest input_handler_->smooth_scroll_enabled_ = value; } + void SetMouseWheelGesturesOn(bool value) { + input_handler_->set_use_gesture_events_for_mouse_wheel(value); + } + + base::HistogramTester& histogram_tester() { + return histogram_tester_; + } + protected: const bool synchronous_root_scroll_; const bool install_synchronous_handler_; testing::StrictMock<MockInputHandler> mock_input_handler_; testing::StrictMock<MockSynchronousInputHandler> mock_synchronous_input_handler_; - scoped_ptr<ui::InputHandlerProxy> input_handler_; + scoped_ptr<TestInputHandlerProxy> input_handler_; testing::StrictMock<MockInputHandlerProxyClient> mock_client_; WebGestureEvent gesture_; InputHandlerProxy::EventDisposition expected_disposition_; + base::HistogramTester histogram_tester_; cc::InputHandlerScrollResult scroll_result_did_scroll_; cc::InputHandlerScrollResult scroll_result_did_not_scroll_; }; TEST_P(InputHandlerProxyTest, MouseWheelByPageMainThread) { expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + SetMouseWheelGesturesOn(false); WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; wheel.scrollByPage = true; @@ -380,6 +419,7 @@ TEST_P(InputHandlerProxyTest, MouseWheelByPageMainThread) { TEST_P(InputHandlerProxyTest, MouseWheelWithCtrlNotScroll) { expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + SetMouseWheelGesturesOn(false); WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; wheel.modifiers = WebInputEvent::ControlKey; @@ -388,6 +428,45 @@ TEST_P(InputHandlerProxyTest, MouseWheelWithCtrlNotScroll) { VERIFY_AND_RESET_MOCKS(); } +TEST_P(InputHandlerProxyTest, MouseWheelNoListener) { + expected_disposition_ = InputHandlerProxy::DROP_EVENT; + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); + + WebMouseWheelEvent wheel; + wheel.type = WebInputEvent::MouseWheel; + wheel.modifiers = WebInputEvent::ControlKey; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); + VERIFY_AND_RESET_MOCKS(); +} + +TEST_P(InputHandlerProxyTest, MouseWheelPassiveListener) { + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); + + WebMouseWheelEvent wheel; + wheel.type = WebInputEvent::MouseWheel; + wheel.modifiers = WebInputEvent::ControlKey; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); + VERIFY_AND_RESET_MOCKS(); +} + +TEST_P(InputHandlerProxyTest, MouseWheelBlockingListener) { + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); + + WebMouseWheelEvent wheel; + wheel.type = WebInputEvent::MouseWheel; + wheel.modifiers = WebInputEvent::ControlKey; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); + VERIFY_AND_RESET_MOCKS(); +} + // Mac does not smooth scroll wheel events (crbug.com/574283). #if !defined(OS_MACOSX) TEST_P(InputHandlerProxyTest, MouseWheelWithPreciseScrollingDeltas) { @@ -395,6 +474,7 @@ TEST_P(InputHandlerProxyTest, MouseWheelWithPreciseScrollingDeltas) { TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelWithPreciseScrollingDeltas) { #endif SetSmoothScrollEnabled(true); + SetMouseWheelGesturesOn(false); expected_disposition_ = InputHandlerProxy::DID_HANDLE; WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; @@ -404,7 +484,7 @@ TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelWithPreciseScrollingDeltas) { // Smooth scroll because hasPreciseScrollingDeltas is set to false. wheel.hasPreciseScrollingDeltas = false; EXPECT_CALL(mock_input_handler_, ScrollAnimated(::testing::_, ::testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); VERIFY_AND_RESET_MOCKS(); @@ -412,7 +492,7 @@ TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelWithPreciseScrollingDeltas) { // No smooth scroll because hasPreciseScrollingDeltas is set to true. wheel.hasPreciseScrollingDeltas = true; EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollBy(::testing::_)) .WillOnce(testing::Return(scroll_result_did_scroll_)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); @@ -428,12 +508,13 @@ TEST_P(InputHandlerProxyTest, MouseWheelScrollIgnored) { TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelScrollIgnored) { #endif SetSmoothScrollEnabled(true); + SetMouseWheelGesturesOn(false); expected_disposition_ = InputHandlerProxy::DROP_EVENT; WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; EXPECT_CALL(mock_input_handler_, ScrollAnimated(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_IGNORED)); + .WillOnce(testing::Return(kScrollIgnoredScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); VERIFY_AND_RESET_MOCKS(); @@ -445,7 +526,7 @@ TEST_P(InputHandlerProxyTest, GestureScrollStarted) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_,input_handler_->HandleInputEvent(gesture_)); @@ -492,7 +573,7 @@ TEST_P(InputHandlerProxyTest, GestureScrollOnMainThread) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD)); + .WillOnce(testing::Return(kMainThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -524,7 +605,7 @@ TEST_P(InputHandlerProxyTest, GestureScrollIgnored) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_IGNORED)); + .WillOnce(testing::Return(kScrollIgnoredScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -538,13 +619,66 @@ TEST_P(InputHandlerProxyTest, GestureScrollIgnored) { VERIFY_AND_RESET_MOCKS(); } +TEST_P(InputHandlerProxyTest, GestureScrollByPage) { + // We should send all events to the widget for this gesture. + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + VERIFY_AND_RESET_MOCKS(); + + gesture_.type = WebInputEvent::GestureScrollBegin; + gesture_.data.scrollBegin.deltaHintUnits = WebGestureEvent::ScrollUnits::Page; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); + + gesture_.type = WebInputEvent::GestureScrollUpdate; + gesture_.data.scrollUpdate.deltaY = 1; + gesture_.data.scrollUpdate.deltaUnits = WebGestureEvent::ScrollUnits::Page; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); + + gesture_.type = WebInputEvent::GestureScrollEnd; + gesture_.data.scrollUpdate.deltaY = 0; + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) + .WillOnce(testing::Return()); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); +} + +// Mac does not smooth scroll wheel events (crbug.com/574283). +#if !defined(OS_MACOSX) +TEST_P(InputHandlerProxyTest, GestureScrollByCoarsePixels) { +#else +TEST_P(InputHandlerProxyTest, DISABLED_GestureScrollByCoarsePixels) { +#endif + SetSmoothScrollEnabled(true); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + + gesture_.type = WebInputEvent::GestureScrollBegin; + gesture_.data.scrollBegin.deltaHintUnits = + WebGestureEvent::ScrollUnits::Pixels; + EXPECT_CALL(mock_input_handler_, ScrollAnimatedBegin(::testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + gesture_.type = WebInputEvent::GestureScrollUpdate; + gesture_.data.scrollUpdate.deltaUnits = WebGestureEvent::ScrollUnits::Pixels; + + EXPECT_CALL(mock_input_handler_, ScrollAnimated(::testing::_, ::testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); +} + TEST_P(InputHandlerProxyTest, GestureScrollBeginThatTargetViewport) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, RootScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.data.scrollBegin.targetViewport = true; @@ -559,8 +693,9 @@ TEST_P(InputHandlerProxyTest, GesturePinch) { VERIFY_AND_RESET_MOCKS(); gesture_.type = WebInputEvent::GesturePinchBegin; - EXPECT_CALL(mock_input_handler_, HaveWheelEventHandlersAt(testing::_)) - .WillOnce(testing::Return(false)); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, PinchGestureBegin()); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -608,8 +743,9 @@ TEST_P(InputHandlerProxyTest, GesturePinchWithWheelHandler) { VERIFY_AND_RESET_MOCKS(); gesture_.type = WebInputEvent::GesturePinchBegin; - EXPECT_CALL(mock_input_handler_, HaveWheelEventHandlersAt(testing::_)) - .WillOnce(testing::Return(true)); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -640,7 +776,7 @@ TEST_P(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD)); + .WillOnce(testing::Return(kMainThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -657,8 +793,9 @@ TEST_P(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) { VERIFY_AND_RESET_MOCKS(); gesture_.type = WebInputEvent::GesturePinchBegin; - EXPECT_CALL(mock_input_handler_, HaveWheelEventHandlersAt(testing::_)) - .WillOnce(testing::Return(false)); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, PinchGestureBegin()); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -717,7 +854,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchpad) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); @@ -740,7 +877,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingOnMainThreadTouchpad) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD)); + .WillOnce(testing::Return(kMainThreadScrollState)); gesture_.type = WebInputEvent::GestureFlingStart; gesture_.sourceDevice = blink::WebGestureDeviceTouchpad; @@ -764,7 +901,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingIgnoredTouchpad) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_IGNORED)); + .WillOnce(testing::Return(kScrollIgnoredScrollState)); gesture_.type = WebInputEvent::GestureFlingStart; gesture_.sourceDevice = blink::WebGestureDeviceTouchpad; @@ -801,11 +938,12 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); + // The first animate call should let us pick up an animation start time, but // we shouldn't actually move anywhere just yet. The first frame after the // fling start will typically include the last scroll from the gesture that @@ -821,8 +959,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) { // The second call should start scrolling in the -X direction. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( mock_input_handler_, ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) @@ -838,8 +979,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) { // We also should pass the current fling parameters out to the client so the // rest of the fling can be // transferred to the main thread. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD)); + .WillOnce(testing::Return(kMainThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); // Expected wheel fling animation parameters: @@ -888,6 +1032,97 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) { VERIFY_AND_RESET_MOCKS(); } +TEST_P(InputHandlerProxyTest, GestureFlingPassiveListener) { + // We shouldn't send any events to the widget for this gesture. + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + VERIFY_AND_RESET_MOCKS(); + + // On the fling start, we should schedule an animation but not actually start + // scrolling. + gesture_.type = WebInputEvent::GestureFlingStart; + WebFloatPoint fling_delta = WebFloatPoint(1000, 0); + WebPoint fling_point = WebPoint(7, 13); + WebPoint fling_global_point = WebPoint(17, 23); + // Note that for trackpad, wheel events with the Control modifier are + // special (reserved for zoom), so don't set that here. + int modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey; + gesture_ = CreateFling(blink::WebGestureDeviceTouchpad, fling_delta, + fling_point, fling_global_point, modifiers); + EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); + + // The first animate call should let us pick up an animation start time, but + // we shouldn't actually move anywhere just yet. The first frame after the + // fling start will typically include the last scroll from the gesture that + // lead to the scroll (either wheel or gesture scroll), so there should be no + // visible hitch. + EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .Times(0); + base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); + Animate(time); + + VERIFY_AND_RESET_MOCKS(); + + // The second call should punt the fling to the main thread + // because of a passive event listener. + EXPECT_SET_NEEDS_ANIMATE_INPUT(0); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .Times(0); + EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); + // Expected wheel fling animation parameters: + // *) fling_delta and fling_point should match the original GestureFlingStart + // event + // *) startTime should be 10 to match the time parameter of the first + // Animate() call after the GestureFlingStart + EXPECT_CALL( + mock_client_, + TransferActiveWheelFlingAnimation(testing::AllOf( + testing::Field(&WebActiveWheelFlingParameters::delta, + testing::Eq(fling_delta)), + testing::Field(&WebActiveWheelFlingParameters::point, + testing::Eq(fling_point)), + testing::Field(&WebActiveWheelFlingParameters::globalPoint, + testing::Eq(fling_global_point)), + testing::Field(&WebActiveWheelFlingParameters::modifiers, + testing::Eq(modifiers)), + testing::Field(&WebActiveWheelFlingParameters::startTime, + testing::Eq(10)), + testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, + testing::_)))); + time += base::TimeDelta::FromMilliseconds(100); + Animate(time); + + VERIFY_AND_RESET_MOCKS(); + + // Since we've aborted the fling, the next animation should be a no-op and + // should not result in another + // frame being requested. + EXPECT_SET_NEEDS_ANIMATE_INPUT(0); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .Times(0); + time += base::TimeDelta::FromMilliseconds(100); + Animate(time); + + // Since we've transferred the fling to the main thread, we need to pass the + // next GestureFlingCancel to the main + // thread as well. + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + gesture_.type = WebInputEvent::GestureFlingCancel; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); +} + TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; @@ -907,10 +1142,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); // Start the fling animation at time 10. This shouldn't actually scroll, just @@ -925,8 +1159,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { // The second call should start scrolling in the -X direction. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( mock_input_handler_, ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) @@ -942,8 +1179,12 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { // We also should pass the current fling parameters out to the client so the // rest of the fling can be // transferred to the main thread. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD)); + .WillOnce(testing::Return(kMainThreadScrollState)); + EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); @@ -1007,7 +1248,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); expected_disposition_ = InputHandlerProxy::DID_HANDLE; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1025,8 +1266,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { // Tick the second fling once normally. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( mock_input_handler_, ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) @@ -1038,8 +1282,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { VERIFY_AND_RESET_MOCKS(); // Then abort the second fling. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD)); + .WillOnce(testing::Return(kMainThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); @@ -1072,7 +1319,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchscreen) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1080,7 +1327,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchscreen) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); gesture_.type = WebInputEvent::GestureFlingStart; @@ -1106,7 +1353,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingOnMainThreadTouchscreen) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD)); + .WillOnce(testing::Return(kMainThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1133,7 +1380,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; @@ -1145,7 +1392,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) { // Flings ignored by the InputHandler should be dropped, signalling the end // of the touch scroll sequence. EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_IGNORED)); + .WillOnce(testing::Return(kScrollIgnoredScrollState)); gesture_.type = WebInputEvent::GestureFlingStart; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; @@ -1157,7 +1404,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) { // GestureFlingCancel, as the original GestureFlingStart was dropped. expected_disposition_ = InputHandlerProxy::DID_HANDLE; EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1171,7 +1418,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchscreen) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; @@ -1193,7 +1440,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchscreen) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -1232,7 +1479,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingWithValidTimestamp) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; @@ -1256,7 +1503,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingWithValidTimestamp) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -1288,7 +1535,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingWithInvalidTimestamp) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; @@ -1315,7 +1562,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingWithInvalidTimestamp) { gesture_.modifiers = modifiers; EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -1353,7 +1600,7 @@ TEST_P(InputHandlerProxyTest, GestureScrollOnImplThreadFlagClearedAfterFling) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1378,7 +1625,7 @@ TEST_P(InputHandlerProxyTest, GestureScrollOnImplThreadFlagClearedAfterFling) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); // |gesture_scroll_on_impl_thread_| should still be true after @@ -1426,7 +1673,7 @@ TEST_P(InputHandlerProxyTest, VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1448,7 +1695,7 @@ TEST_P(InputHandlerProxyTest, fling_point, fling_global_point, modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); // |gesture_scroll_on_impl_thread_| should still be true after @@ -1461,7 +1708,7 @@ TEST_P(InputHandlerProxyTest, // result, this scroll begin will cancel the previous fling. EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1484,7 +1731,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) { gesture_.data.flingStart.velocityX = fling_delta.x; gesture_.data.flingStart.velocityY = fling_delta.y; EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1497,8 +1744,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) { VERIFY_AND_RESET_MOCKS(); // The second animate starts scrolling in the positive X and Y directions. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( mock_input_handler_, ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) @@ -1516,8 +1766,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) { overscroll.did_overscroll_root = true; overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100); overscroll.unused_scroll_delta = gfx::Vector2dF(0, 10); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( mock_input_handler_, ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) @@ -1537,8 +1790,11 @@ TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) { // The next call to animate will no longer scroll vertically. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( mock_input_handler_, ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Eq(0)))) @@ -1555,7 +1811,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingNotCancelledBySmallTimeDelta) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; @@ -1579,7 +1835,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingNotCancelledBySmallTimeDelta) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -1636,7 +1892,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1649,7 +1905,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { gesture_.data.flingStart.velocityX = fling_delta.x; gesture_.data.flingStart.velocityY = fling_delta.y; EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -1734,9 +1990,15 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestNegative) { expected_disposition_ = InputHandlerProxy::DROP_EVENT; VERIFY_AND_RESET_MOCKS(); - EXPECT_CALL(mock_input_handler_, - DoTouchEventsBlockScrollAt( - testing::Property(&gfx::Point::x, testing::Gt(0)))) + EXPECT_CALL( + mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); + EXPECT_CALL( + mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kTouchEndOrCancel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); + EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt(testing::_)) .WillOnce(testing::Return(false)); EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt( @@ -1784,13 +2046,70 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPositive) { VERIFY_AND_RESET_MOCKS(); } +TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPassivePositive) { + // One of the touch points is on a touch-region. So the event should be sent + // to the main thread. + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL( + mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) + .WillRepeatedly(testing::Return(cc::EventListenerProperties::kPassive)); + EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt(testing::_)) + .WillRepeatedly(testing::Return(false)); + + WebTouchEvent touch; + touch.type = WebInputEvent::TouchStart; + + touch.touchesLength = 3; + touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 0, 0); + touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 10, 10); + touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::StatePressed, -10, 10); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch)); + + VERIFY_AND_RESET_MOCKS(); +} + +TEST_P(InputHandlerProxyTest, TouchStartPassiveAndTouchEndBlocking) { + // The touch start is not in a touch-region but there is a touch end handler + // so to maintain targeting we need to dispatch the touch start as + // non-blocking but drop all touch moves. + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL( + mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); + EXPECT_CALL( + mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kTouchEndOrCancel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); + EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt(testing::_)) + .WillOnce(testing::Return(false)); + + WebTouchEvent touch; + touch.type = WebInputEvent::TouchStart; + touch.touchesLength = 1; + touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 0, 0); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch)); + + touch.type = WebInputEvent::TouchMove; + touch.touchesLength = 1; + touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 10, 10); + EXPECT_EQ(InputHandlerProxy::DROP_EVENT, + input_handler_->HandleInputEvent(touch)); + VERIFY_AND_RESET_MOCKS(); +} + TEST_P(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1811,7 +2130,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) { gesture_.data.flingStart.velocityX = fling_delta.x; gesture_.data.flingStart.velocityY = fling_delta.y; EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); @@ -1836,13 +2155,78 @@ TEST_P(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) { EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); } +TEST_P(InputHandlerProxyTest, GestureFlingCancelledByWheelEvent) { + // We shouldn't send any events to the widget for this gesture. + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + + gesture_.type = WebInputEvent::GestureScrollBegin; + gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // Wheel events received during a scroll shouldn't cancel the fling, but will + // cause scrolling. + cc::InputHandlerScrollResult result; + + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); + + WebMouseWheelEvent wheel_event; + wheel_event.type = WebInputEvent::MouseWheel; + input_handler_->HandleInputEvent(wheel_event); + EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // On the fling start, animation should be scheduled, but no scrolling occurs. + gesture_.type = WebInputEvent::GestureFlingStart; + WebFloatPoint fling_delta = WebFloatPoint(100, 100); + gesture_.data.flingStart.velocityX = fling_delta.x; + gesture_.data.flingStart.velocityY = fling_delta.y; + EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) + .WillOnce(testing::Return(kImplThreadScrollState)); + EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // Wheel events received during a fling should cancel the active fling, and + // cause a scroll. + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); + + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); + + + input_handler_->HandleInputEvent(wheel_event); + EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // The call to animate should have no effect, as the fling was cancelled. + base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); + Animate(time); + VERIFY_AND_RESET_MOCKS(); + + // A fling cancel should be dropped, as there is nothing to cancel. + gesture_.type = WebInputEvent::GestureFlingCancel; + EXPECT_EQ(InputHandlerProxy::DROP_EVENT, + input_handler_->HandleInputEvent(gesture_)); + EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); +} + TEST_P(InputHandlerProxyTest, GestureFlingWithNegativeTimeDelta) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.type = WebInputEvent::GestureScrollBegin; gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; @@ -1866,7 +2250,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingWithNegativeTimeDelta) { modifiers); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -2051,7 +2435,7 @@ TEST_P(InputHandlerProxyTest, NoFlingBoostIfScrollTargetsDifferentLayer) { .WillOnce(testing::Return(false)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); time += dt; gesture_.timeStampSeconds = InSecondsF(time); @@ -2092,7 +2476,7 @@ TEST_P(InputHandlerProxyTest, NoFlingBoostIfScrollDelayed) { time += base::TimeDelta::FromMilliseconds(100); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); Animate(time); VERIFY_AND_RESET_MOCKS(); @@ -2137,7 +2521,7 @@ TEST_P(InputHandlerProxyTest, NoFlingBoostIfNotAnimated) { gesture_.data.scrollUpdate.deltaY = -40; EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)) .WillOnce(testing::Return(scroll_result_did_scroll_)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -2220,7 +2604,7 @@ TEST_P(InputHandlerProxyTest, NoFlingBoostIfScrollInDifferentDirection) { gesture_.data.scrollUpdate.deltaX = -fling_delta.x; EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Eq(fling_delta.x)))) @@ -2327,7 +2711,7 @@ TEST_P(InputHandlerProxyTest, FlingBoostTerminatedDuringScrollSequence) { .WillOnce(testing::Return(scroll_result_did_not_scroll_)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); Animate(time); VERIFY_AND_RESET_MOCKS(); @@ -2361,7 +2745,7 @@ TEST_P(InputHandlerProxyTest, DidReceiveInputEvent_ForFling) { testing::StrictMock<MockInputHandlerProxyClientWithDidAnimateForInput> mock_client; input_handler_.reset( - new ui::InputHandlerProxy( + new TestInputHandlerProxy( &mock_input_handler_, &mock_client)); if (install_synchronous_handler_) { EXPECT_CALL(mock_input_handler_, RequestUpdateForSynchronousInputHandler()) @@ -2377,7 +2761,7 @@ TEST_P(InputHandlerProxyTest, DidReceiveInputEvent_ForFling) { gesture_.data.flingStart.velocityY = fling_delta.y; EXPECT_SET_NEEDS_ANIMATE_INPUT(1); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED)); + .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_EQ(InputHandlerProxy::DID_HANDLE, input_handler_->HandleInputEvent(gesture_)); @@ -2460,6 +2844,21 @@ TEST(SynchronousInputHandlerProxyTest, SetOffset) { testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler); } +TEST_P(InputHandlerProxyTest, MainThreadScrollingMouseWheelHistograms) { + input_handler_->RecordMainThreadScrollingReasonsForTest( + WebInputEvent::MouseWheel, + cc::MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects | + cc::MainThreadScrollingReason::kThreadedScrollingDisabled | + cc::MainThreadScrollingReason::kPageOverlay | + cc::MainThreadScrollingReason::kAnimatingScrollOnMainThread); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), + testing::ElementsAre(base::Bucket(1, 1), base::Bucket(3, 1), + base::Bucket(5, 1), base::Bucket(14, 1))); +} + + INSTANTIATE_TEST_CASE_P(AnimateInput, InputHandlerProxyTest, testing::ValuesIn(test_types)); diff --git a/chromium/ui/events/blink/input_scroll_elasticity_controller.cc b/chromium/ui/events/blink/input_scroll_elasticity_controller.cc index d063093cc26..dedf7de60d5 100644 --- a/chromium/ui/events/blink/input_scroll_elasticity_controller.cc +++ b/chromium/ui/events/blink/input_scroll_elasticity_controller.cc @@ -167,6 +167,71 @@ void InputScrollElasticityController::ObserveWheelEventAndResult( } } +void InputScrollElasticityController::ObserveGestureEventAndResult( + const blink::WebGestureEvent& gesture_event, + const cc::InputHandlerScrollResult& scroll_result) { + base::TimeTicks event_timestamp = + base::TimeTicks() + + base::TimeDelta::FromSecondsD(gesture_event.timeStampSeconds); + + switch (gesture_event.type) { + case blink::WebInputEvent::GestureScrollBegin: { + if (gesture_event.data.scrollBegin.synthetic) + return; + if (gesture_event.data.scrollBegin.inertial) { + if (state_ == kStateInactive) + state_ = kStateMomentumScroll; + } else if (gesture_event.data.scrollBegin.deltaHintUnits == + blink::WebGestureEvent::PrecisePixels) { + scroll_velocity = gfx::Vector2dF(); + last_scroll_event_timestamp_ = base::TimeTicks(); + state_ = kStateActiveScroll; + pending_overscroll_delta_ = gfx::Vector2dF(); + } + break; + } + case blink::WebInputEvent::GestureScrollUpdate: { + gfx::Vector2dF event_delta(-gesture_event.data.scrollUpdate.deltaX, + -gesture_event.data.scrollUpdate.deltaY); + switch (state_) { + case kStateMomentumAnimated: + case kStateInactive: + break; + case kStateActiveScroll: + case kStateMomentumScroll: + UpdateVelocity(event_delta, event_timestamp); + Overscroll(event_delta, scroll_result.unused_scroll_delta); + if (gesture_event.data.scrollUpdate.inertial && + !helper_->StretchAmount().IsZero()) { + EnterStateMomentumAnimated(event_timestamp); + } + break; + } + break; + } + case blink::WebInputEvent::GestureScrollEnd: { + if (gesture_event.data.scrollEnd.synthetic) + return; + switch (state_) { + case kStateMomentumAnimated: + case kStateInactive: + break; + case kStateActiveScroll: + case kStateMomentumScroll: + if (helper_->StretchAmount().IsZero()) { + EnterStateInactive(); + } else { + EnterStateMomentumAnimated(event_timestamp); + } + break; + } + break; + } + default: + break; + } +} + void InputScrollElasticityController::UpdateVelocity( const gfx::Vector2dF& event_delta, const base::TimeTicks& event_timestamp) { diff --git a/chromium/ui/events/blink/input_scroll_elasticity_controller.h b/chromium/ui/events/blink/input_scroll_elasticity_controller.h index 8c6468109c8..915b2e88af1 100644 --- a/chromium/ui/events/blink/input_scroll_elasticity_controller.h +++ b/chromium/ui/events/blink/input_scroll_elasticity_controller.h @@ -58,6 +58,15 @@ class InputScrollElasticityController { void ObserveWheelEventAndResult( const blink::WebMouseWheelEvent& wheel_event, const cc::InputHandlerScrollResult& scroll_result); + // Update the overscroll state based a gesture event that has been processed. + // Note that this assumes that all events are coming from a single input + // device. If the user simultaneously uses multiple input devices, Cocoa may + // not correctly pass all the gesture begin and end events. In this case, + // this class may disregard some scrolls that come in at unexpected times. + void ObserveGestureEventAndResult( + const blink::WebGestureEvent& gesture_event, + const cc::InputHandlerScrollResult& scroll_result); + void Animate(base::TimeTicks time); void ReconcileStretchAndScroll(); 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 82b94343f8e..4ce7b9494ed 100644 --- a/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc +++ b/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc @@ -99,6 +99,53 @@ class ScrollElasticityControllerTest : public testing::Test { input_event_count_ += 1; } + void SendGestureScrollBegin(bool inertial) { + blink::WebGestureEvent event; + event.sourceDevice = blink::WebGestureDeviceTouchpad; + event.type = blink::WebInputEvent::GestureScrollBegin; + event.data.scrollBegin.inertial = inertial; + TickCurrentTime(); + event.timeStampSeconds = (current_time_ - base::TimeTicks()).InSecondsF(); + + controller_.ObserveGestureEventAndResult(event, + cc::InputHandlerScrollResult()); + input_event_count_ += 1; + } + + void SendGestureScrollUpdate( + bool inertial, + const gfx::Vector2dF& event_delta = gfx::Vector2dF(), + const gfx::Vector2dF& overscroll_delta = gfx::Vector2dF()) { + blink::WebGestureEvent event; + event.sourceDevice = blink::WebGestureDeviceTouchpad; + event.type = blink::WebInputEvent::GestureScrollUpdate; + event.data.scrollUpdate.inertial = inertial; + event.data.scrollUpdate.deltaX = -event_delta.x(); + event.data.scrollUpdate.deltaY = -event_delta.y(); + TickCurrentTime(); + event.timeStampSeconds = (current_time_ - base::TimeTicks()).InSecondsF(); + + cc::InputHandlerScrollResult scroll_result; + scroll_result.did_overscroll_root = !overscroll_delta.IsZero(); + scroll_result.unused_scroll_delta = overscroll_delta; + + controller_.ObserveGestureEventAndResult(event, scroll_result); + input_event_count_ += 1; + } + + void SendGestureScrollEnd() { + blink::WebGestureEvent event; + event.sourceDevice = blink::WebGestureDeviceTouchpad; + event.type = blink::WebInputEvent::GestureScrollEnd; + + TickCurrentTime(); + event.timeStampSeconds = (current_time_ - base::TimeTicks()).InSecondsF(); + + controller_.ObserveGestureEventAndResult(event, + cc::InputHandlerScrollResult()); + input_event_count_ += 1; + } + const base::TimeTicks& TickCurrentTime() { current_time_ += base::TimeDelta::FromSecondsD(1 / 60.f); return current_time_; @@ -148,6 +195,40 @@ TEST_F(ScrollElasticityControllerTest, Axis) { EXPECT_EQ(0, helper_.request_begin_frame_count()); } +// Verify that stretching only occurs in one axis at a time, and that it +// is biased to the Y axis. +TEST_F(ScrollElasticityControllerTest, GestureBased_Axis) { + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0), + gfx::ScrollOffset(0, 0)); + + // If we push equally in the X and Y directions, we should see a stretch only + // in the Y direction. + SendGestureScrollBegin(false); + SendGestureScrollUpdate(false, gfx::Vector2dF(10, 10), + gfx::Vector2dF(10, 10)); + EXPECT_EQ(1, helper_.set_stretch_amount_count()); + EXPECT_EQ(0.f, helper_.StretchAmount().x()); + EXPECT_LT(0.f, helper_.StretchAmount().y()); + helper_.SetStretchAmount(gfx::Vector2dF()); + EXPECT_EQ(2, helper_.set_stretch_amount_count()); + SendGestureScrollEnd(); + EXPECT_EQ(0, helper_.request_begin_frame_count()); + + // If we push more in the X direction than the Y direction, we should see a + // stretch only in the X direction. This decision should be based on the + // input delta, not the actual overscroll delta. + SendGestureScrollBegin(false); + SendGestureScrollUpdate(false, gfx::Vector2dF(-25, 10), + gfx::Vector2dF(-25, 40)); + EXPECT_EQ(3, helper_.set_stretch_amount_count()); + EXPECT_GT(0.f, helper_.StretchAmount().x()); + EXPECT_EQ(0.f, helper_.StretchAmount().y()); + helper_.SetStretchAmount(gfx::Vector2dF()); + EXPECT_EQ(4, helper_.set_stretch_amount_count()); + SendGestureScrollEnd(); + EXPECT_EQ(0, helper_.request_begin_frame_count()); +} + // Verify that we need a total overscroll delta of at least 10 in a pinned // direction before we start stretching. TEST_F(ScrollElasticityControllerTest, MinimumDeltaBeforeStretch) { @@ -201,7 +282,54 @@ TEST_F(ScrollElasticityControllerTest, MinimumDeltaBeforeStretch) { EXPECT_EQ(1, helper_.request_begin_frame_count()); } -// Verify that an stretch caused by a momentum scroll will switch to the +// Verify that we need a total overscroll delta of at least 10 in a pinned +// direction before we start stretching. +TEST_F(ScrollElasticityControllerTest, GestureBased_MinimumDeltaBeforeStretch) { + // We should not start stretching while we are not pinned in the direction + // of the scroll (even if there is an overscroll delta). We have to wait for + // the regular scroll to eat all of the events. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), + gfx::ScrollOffset(10, 10)); + SendGestureScrollBegin(false); + SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 10)); + SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 10)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + + // Now pin the -X and +Y direction. The first event will not generate a + // stretch + // because it is below the delta threshold of 10. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 10), + gfx::ScrollOffset(10, 10)); + SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 8)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + + // Make the next scroll be in the -X direction more than the +Y direction, + // which will erase the memory of the previous unused delta of 8. + SendGestureScrollUpdate(false, gfx::Vector2dF(-10, 5), + gfx::Vector2dF(-8, 10)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + + // Now push against the pinned +Y direction again by 8. We reset the + // previous delta, so this will not generate a stretch. + SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 8)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + + // Push against +Y by another 8. This gets us above the delta threshold of + // 10, so we should now have had the stretch set, and it should be in the + // +Y direction. The scroll in the -X direction should have been forgotten. + SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 8)); + EXPECT_EQ(1, helper_.set_stretch_amount_count()); + EXPECT_EQ(0.f, helper_.StretchAmount().x()); + EXPECT_LT(0.f, helper_.StretchAmount().y()); + + // End the gesture. Because there is a non-zero stretch, we should be in the + // animated state, and should have had a frame requested. + EXPECT_EQ(0, helper_.request_begin_frame_count()); + SendGestureScrollEnd(); + EXPECT_EQ(1, helper_.request_begin_frame_count()); +} + +// Verify that a stretch caused by a momentum scroll will switch to the // animating mode, where input events are ignored, and the stretch is updated // while animating. TEST_F(ScrollElasticityControllerTest, MomentumAnimate) { @@ -297,7 +425,93 @@ TEST_F(ScrollElasticityControllerTest, MomentumAnimate) { EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count()); } -// Verify that an stretch opposing a scroll is correctly resolved. +// Verify that a stretch caused by a momentum scroll will switch to the +// animating mode, where input events are ignored, and the stretch is updated +// while animating. +TEST_F(ScrollElasticityControllerTest, GestureBased_MomentumAnimate) { + // Do an active scroll, then switch to the momentum phase and scroll for a + // bit. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), + gfx::ScrollOffset(10, 10)); + SendGestureScrollBegin(false); + SendGestureScrollUpdate(false, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0)); + SendGestureScrollUpdate(false, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0)); + SendGestureScrollUpdate(false, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0)); + SendGestureScrollEnd(); + SendGestureScrollBegin(true); + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0)); + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0)); + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + + // Hit the -Y edge and overscroll slightly, but not enough to go over the + // threshold to cause a stretch. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 0), + gfx::ScrollOffset(10, 10)); + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -8)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + EXPECT_EQ(0, helper_.request_begin_frame_count()); + + // Take another step, this time going over the threshold. This should update + // the stretch amount, and then switch to the animating mode. + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80)); + EXPECT_EQ(1, helper_.set_stretch_amount_count()); + EXPECT_EQ(1, helper_.request_begin_frame_count()); + EXPECT_GT(-1.f, helper_.StretchAmount().y()); + + // Subsequent momentum events should do nothing. + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80)); + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80)); + SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80)); + SendGestureScrollEnd(); + EXPECT_EQ(1, helper_.set_stretch_amount_count()); + EXPECT_EQ(1, helper_.request_begin_frame_count()); + + // Subsequent animate events should update the stretch amount and request + // another frame. + TickCurrentTimeAndAnimate(); + EXPECT_EQ(2, helper_.set_stretch_amount_count()); + EXPECT_EQ(2, helper_.request_begin_frame_count()); + EXPECT_GT(-1.f, helper_.StretchAmount().y()); + + // Touching the trackpad (a PhaseMayBegin event) should disable animation. + SendGestureScrollBegin(false); + TickCurrentTimeAndAnimate(); + EXPECT_EQ(2, helper_.set_stretch_amount_count()); + EXPECT_EQ(2, helper_.request_begin_frame_count()); + + // Releasing the trackpad should re-enable animation. + SendGestureScrollEnd(); + EXPECT_EQ(2, helper_.set_stretch_amount_count()); + EXPECT_EQ(3, helper_.request_begin_frame_count()); + TickCurrentTimeAndAnimate(); + EXPECT_EQ(3, helper_.set_stretch_amount_count()); + EXPECT_EQ(4, helper_.request_begin_frame_count()); + + // Keep animating frames until the stretch returns to rest. + int stretch_count = 3; + int begin_frame_count = 4; + while (1) { + TickCurrentTimeAndAnimate(); + if (helper_.StretchAmount().IsZero()) { + stretch_count += 1; + EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count()); + EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count()); + break; + } + stretch_count += 1; + begin_frame_count += 1; + EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count()); + EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count()); + } + + // After coming to rest, no subsequent animate calls change anything. + TickCurrentTimeAndAnimate(); + EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count()); + EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count()); +} + +// Verify that a stretch opposing a scroll is correctly resolved. TEST_F(ScrollElasticityControllerTest, ReconcileStretchAndScroll) { SendMouseWheelEvent(PhaseBegan, PhaseNone); @@ -334,14 +548,50 @@ TEST_F(ScrollElasticityControllerTest, ReconcileStretchAndScroll) { EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(7, 8)); } +// Verify that a stretch opposing a scroll is correctly resolved. +TEST_F(ScrollElasticityControllerTest, GestureBased_ReconcileStretchAndScroll) { + SendGestureScrollBegin(false); + + // Verify completely knocking out the scroll in the -Y direction. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), + gfx::ScrollOffset(10, 10)); + helper_.SetStretchAmount(gfx::Vector2dF(0, -10)); + controller_.ReconcileStretchAndScroll(); + EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, -5)); + EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(5, 0)); + + // Verify partially knocking out the scroll in the -Y direction. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 8), + gfx::ScrollOffset(10, 10)); + helper_.SetStretchAmount(gfx::Vector2dF(0, -5)); + controller_.ReconcileStretchAndScroll(); + EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); + EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(5, 3)); + + // Verify completely knocking out the scroll in the +X direction. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), + gfx::ScrollOffset(10, 10)); + helper_.SetStretchAmount(gfx::Vector2dF(10, 0)); + controller_.ReconcileStretchAndScroll(); + EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(5, 0)); + EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(10, 5)); + + // Verify partially knocking out the scroll in the +X and +Y directions. + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(2, 3), + gfx::ScrollOffset(10, 10)); + helper_.SetStretchAmount(gfx::Vector2dF(5, 5)); + controller_.ReconcileStretchAndScroll(); + EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); + EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(7, 8)); +} + // Verify that stretching only happens when the area is user scrollable. TEST_F(ScrollElasticityControllerTest, UserScrollableRequiredForStretch) { helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0), gfx::ScrollOffset(10, 10)); gfx::Vector2dF delta(0, -15); - // Do an active scroll, and ensure that the stretch amount doesn't change, - // and also that the stretch amount isn't even ever changed. + // Do an active scroll, and ensure that the stretch amount doesn't change. helper_.SetUserScrollable(false); SendMouseWheelEvent(PhaseBegan, PhaseNone); SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta); @@ -384,5 +634,55 @@ TEST_F(ScrollElasticityControllerTest, UserScrollableRequiredForStretch) { EXPECT_GT(ticks_to_zero, 3); } +// Verify that stretching only happens when the area is user scrollable. +TEST_F(ScrollElasticityControllerTest, + GestureBased_UserScrollableRequiredForStretch) { + helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0), + gfx::ScrollOffset(10, 10)); + gfx::Vector2dF delta(0, -15); + + // Do an active scroll, and ensure that the stretch amount doesn't change. + helper_.SetUserScrollable(false); + SendGestureScrollBegin(false); + SendGestureScrollUpdate(false, delta, delta); + SendGestureScrollUpdate(false, delta, delta); + SendGestureScrollEnd(); + EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + SendGestureScrollBegin(true); + SendGestureScrollUpdate(true, delta, delta); + SendGestureScrollUpdate(true, delta, delta); + SendGestureScrollEnd(); + EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); + EXPECT_EQ(0, helper_.set_stretch_amount_count()); + + // Re-enable user scrolling and ensure that stretching is re-enabled. + helper_.SetUserScrollable(true); + SendGestureScrollBegin(false); + SendGestureScrollUpdate(false, delta, delta); + SendGestureScrollUpdate(false, delta, delta); + SendGestureScrollEnd(); + EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); + EXPECT_GT(helper_.set_stretch_amount_count(), 0); + SendGestureScrollBegin(true); + SendGestureScrollUpdate(true, delta, delta); + SendGestureScrollUpdate(true, delta, delta); + SendGestureScrollEnd(); + EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); + EXPECT_GT(helper_.set_stretch_amount_count(), 0); + + // Disable user scrolling and tick the timer until the stretch goes back + // to zero. Ensure that the return to zero doesn't happen immediately. + helper_.SetUserScrollable(false); + int ticks_to_zero = 0; + while (1) { + TickCurrentTimeAndAnimate(); + if (helper_.StretchAmount().IsZero()) + break; + ticks_to_zero += 1; + } + EXPECT_GT(ticks_to_zero, 3); +} + } // namespace } // namespace ui diff --git a/chromium/ui/events/blink/synchronous_input_handler_proxy.h b/chromium/ui/events/blink/synchronous_input_handler_proxy.h index 36485f0d72f..cafd7ce66d1 100644 --- a/chromium/ui/events/blink/synchronous_input_handler_proxy.h +++ b/chromium/ui/events/blink/synchronous_input_handler_proxy.h @@ -8,6 +8,7 @@ #include "base/time/time.h" namespace gfx { +class Point; class ScrollOffset; class SizeF; } @@ -65,6 +66,12 @@ class SynchronousInputHandlerProxy { // from what was sent. virtual void SynchronouslySetRootScrollOffset( const gfx::ScrollOffset& root_offset) = 0; + + // Similar to SetRootScrollOffset above, to control the zoom level, ie scale + // factor. Note |magnify_delta| is an incremental rather than absolute value. + // SynchronousInputHandler should be given back the resulting absolute value. + virtual void SynchronouslyZoomBy(float magnify_delta, + const gfx::Point& anchor) = 0; }; } // namespace ui diff --git a/chromium/ui/events/cocoa/events_mac.mm b/chromium/ui/events/cocoa/events_mac.mm index 79beaf39305..003b8805b80 100644 --- a/chromium/ui/events/cocoa/events_mac.mm +++ b/chromium/ui/events/cocoa/events_mac.mm @@ -178,24 +178,20 @@ int GetTouchId(const base::NativeEvent& native_event) { return 0; } -float GetTouchRadiusX(const base::NativeEvent& native_event) { - NOTIMPLEMENTED(); - return 0.f; -} - -float GetTouchRadiusY(const base::NativeEvent& native_event) { - NOTIMPLEMENTED(); - return 0.f; -} - float GetTouchAngle(const base::NativeEvent& native_event) { NOTIMPLEMENTED(); return 0.f; } -float GetTouchForce(const base::NativeEvent& native_event) { +PointerDetails GetTouchPointerDetailsFromNative( + const base::NativeEvent& native_event) { NOTIMPLEMENTED(); - return 0.f; + return PointerDetails(EventPointerType::POINTER_TYPE_UNKNOWN, + /* radius_x */ 1.0, + /* radius_y */ 1.0, + /* force */ 0.f, + /* tilt_x */ 0.f, + /* tilt_y */ 0.f); } bool GetScrollOffsets(const base::NativeEvent& native_event, diff --git a/chromium/ui/events/cocoa/events_mac_unittest.mm b/chromium/ui/events/cocoa/events_mac_unittest.mm index 8a5d84a0afd..96abb2f06da 100644 --- a/chromium/ui/events/cocoa/events_mac_unittest.mm +++ b/chromium/ui/events/cocoa/events_mac_unittest.mm @@ -9,6 +9,7 @@ #include "base/mac/scoped_cftyperef.h" #import "base/mac/scoped_objc_class_swizzler.h" +#include "base/mac/sdk_forward_declarations.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" @@ -109,7 +110,9 @@ class EventsMacTest : public CocoaTest { // of window coordinates (which also requires flipping). NSPoint window_point = NSPointFromCGPoint(Flip(window_location).ToCGPoint()); - NSPoint screen_point = [test_window() convertBaseToScreen:window_point]; + NSRect window_rect = NSMakeRect(window_point.x, window_point.y, 0, 0); + NSPoint screen_point = + [test_window() convertRectToScreen:window_rect].origin; CGFloat primary_screen_height = NSHeight([[[NSScreen screens] firstObject] frame]); screen_point.y = primary_screen_height - screen_point.y; diff --git a/chromium/ui/events/devices/input_device.cc b/chromium/ui/events/devices/input_device.cc index 02139b120d0..4734edd0280 100644 --- a/chromium/ui/events/devices/input_device.cc +++ b/chromium/ui/events/devices/input_device.cc @@ -32,6 +32,8 @@ InputDevice::InputDevice(int id, vendor_id(vendor), product_id(product) {} +InputDevice::InputDevice(const InputDevice& other) = default; + InputDevice::~InputDevice() { } diff --git a/chromium/ui/events/devices/input_device.h b/chromium/ui/events/devices/input_device.h index c118261ec71..4ef06b38cf8 100644 --- a/chromium/ui/events/devices/input_device.h +++ b/chromium/ui/events/devices/input_device.h @@ -33,6 +33,7 @@ struct EVENTS_DEVICES_EXPORT InputDevice { const base::FilePath& sys_path, uint16_t vendor, uint16_t product); + InputDevice(const InputDevice& other); virtual ~InputDevice(); // ID of the device. This ID is unique between all input devices. 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 0ac4f6d3303..b824aac3440 100644 --- a/chromium/ui/events/devices/x11/device_data_manager_x11.cc +++ b/chromium/ui/events/devices/x11/device_data_manager_x11.cc @@ -218,7 +218,6 @@ bool DeviceDataManagerX11::IsXInput2Available() const { void DeviceDataManagerX11::UpdateDeviceList(Display* display) { cmt_devices_.reset(); touchpads_.reset(); - scrollclass_devices_.reset(); master_pointers_.clear(); for (int i = 0; i < kMaxDeviceNum; ++i) { valuator_count_[i] = 0; @@ -424,14 +423,13 @@ int DeviceDataManagerX11::GetScrollClassEventDetail(const XEvent& xev) const { XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data); if (xievent->sourceid >= kMaxDeviceNum) return SCROLL_TYPE_NO_SCROLL; - if (!scrollclass_devices_[xievent->sourceid]) - return SCROLL_TYPE_NO_SCROLL; int horizontal_id = scroll_data_[xievent->sourceid].horizontal.number; int vertical_id = scroll_data_[xievent->sourceid].vertical.number; - return (XIMaskIsSet(xievent->valuators.mask, horizontal_id) + return (horizontal_id != -1 && + XIMaskIsSet(xievent->valuators.mask, horizontal_id) ? SCROLL_TYPE_HORIZONTAL : 0) | - (XIMaskIsSet(xievent->valuators.mask, vertical_id) + (vertical_id != -1 && XIMaskIsSet(xievent->valuators.mask, vertical_id) ? SCROLL_TYPE_VERTICAL : 0); } @@ -773,6 +771,16 @@ void DeviceDataManagerX11::UpdateScrollClassDevice( int deviceid) { DCHECK(deviceid >= 0 && deviceid < kMaxDeviceNum); ScrollInfo& info = scroll_data_[deviceid]; + + bool legacy_scroll_available = + (scroll_class_info->flags & XIScrollFlagNoEmulation) == 0; + // If the device's highest resolution is lower than the resolution of xinput1 + // then use xinput1's events instead (ie. don't configure smooth scrolling). + if (legacy_scroll_available && + std::abs(scroll_class_info->increment) <= 1.0) { + return; + } + switch (scroll_class_info->scroll_type) { case XIScrollTypeVertical: info.vertical.number = scroll_class_info->number; @@ -787,7 +795,6 @@ void DeviceDataManagerX11::UpdateScrollClassDevice( info.horizontal.seen = false; break; } - scrollclass_devices_[deviceid] = true; } double DeviceDataManagerX11::ExtractAndUpdateScrollOffset( @@ -801,20 +808,6 @@ double DeviceDataManagerX11::ExtractAndUpdateScrollOffset( return offset / axis->increment; } -bool DeviceDataManagerX11::TouchEventNeedsCalibrate(int touch_device_id) const { -#if defined(OS_CHROMEOS) - if (!base::SysInfo::IsRunningOnChromeOS()) - return false; - - const std::vector<TouchscreenDevice>& touch_devices = - ui::DeviceDataManager::GetInstance()->touchscreen_devices(); - std::vector<TouchscreenDevice>::const_iterator it = FindDeviceWithId( - touch_devices.begin(), touch_devices.end(), touch_device_id); - return it != touch_devices.end() && it->type == INPUT_DEVICE_INTERNAL; -#endif // defined(OS_CHROMEOS) - return false; -} - void DeviceDataManagerX11::SetDisabledKeyboardAllowedKeys( scoped_ptr<std::set<KeyboardCode> > excepted_keys) { DCHECK(!excepted_keys.get() || diff --git a/chromium/ui/events/devices/x11/device_data_manager_x11.h b/chromium/ui/events/devices/x11/device_data_manager_x11.h index 0fb81d1faad..751cdd7c404 100644 --- a/chromium/ui/events/devices/x11/device_data_manager_x11.h +++ b/chromium/ui/events/devices/x11/device_data_manager_x11.h @@ -256,8 +256,6 @@ class EVENTS_DEVICES_EXPORT DeviceDataManagerX11 : public DeviceDataManager { DataType type, double value); - bool TouchEventNeedsCalibrate(int touch_device_id) const; - // Sets the keys which are still allowed on a disabled keyboard device. void SetDisabledKeyboardAllowedKeys( scoped_ptr<std::set<KeyboardCode> > excepted_keys); @@ -342,7 +340,6 @@ class EVENTS_DEVICES_EXPORT DeviceDataManagerX11 : public DeviceDataManager { // should be processed. std::bitset<kMaxDeviceNum> cmt_devices_; std::bitset<kMaxDeviceNum> touchpads_; - std::bitset<kMaxDeviceNum> scrollclass_devices_; // List of the master pointer devices. std::vector<int> master_pointers_; diff --git a/chromium/ui/events/event.cc b/chromium/ui/events/event.cc index f4cf7cfd9fa..6548405f179 100644 --- a/chromium/ui/events/event.cc +++ b/chromium/ui/events/event.cc @@ -37,6 +37,43 @@ #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" // nogncheck #endif +#if defined(OS_WIN) +#include "ui/events/keycodes/platform_key_map_win.h" +#endif + +// Support a collection of histograms, perhaps one for each entry in an +// enumeration. This macro manages a block of pointers, adding to a specific +// one by its index. +// +// A typical instantiation looks something like this: +// STATIC_HISTOGRAM_POINTER_GROUP( +// GetHistogramNameForIndex(histogram_index), +// histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta), +// base::Histogram::FactoryGet( +// GetHistogramNameForType(histogram_index), +// MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT, +// base::HistogramBase::kUmaTargetedHistogramFlag)); +// +// Though it seems inefficient to generate the name twice, the first +// instance will be used only for DCHECK builds and the second will +// execute only during the first access to the given index, after which +// the pointer is cached and the name never needed again. +// +// This is defined in this way so that it can be moved unchanged into +// base/metrics/histogram_macros.h if it is useful in other files. +#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index, \ + constant_maximum, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ + do { \ + static base::subtle::AtomicWord atomic_histograms[constant_maximum]; \ + DCHECK_LE(0, index); \ + DCHECK_LT(index, constant_maximum); \ + HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation); \ + } while (0) + namespace { std::string EventTypeName(ui::EventType type) { @@ -59,6 +96,12 @@ std::string EventTypeName(ui::EventType type) { CASE_TYPE(ET_TOUCH_MOVED); CASE_TYPE(ET_TOUCH_CANCELLED); CASE_TYPE(ET_DROP_TARGET_EVENT); + CASE_TYPE(ET_POINTER_DOWN); + CASE_TYPE(ET_POINTER_MOVED); + CASE_TYPE(ET_POINTER_UP); + CASE_TYPE(ET_POINTER_CANCELLED); + CASE_TYPE(ET_POINTER_ENTERED); + CASE_TYPE(ET_POINTER_EXITED); CASE_TYPE(ET_GESTURE_SCROLL_BEGIN); CASE_TYPE(ET_GESTURE_SCROLL_END); CASE_TYPE(ET_GESTURE_SCROLL_UPDATE); @@ -145,6 +188,11 @@ scoped_ptr<Event> Event::Clone(const Event& event) { new GestureEvent(static_cast<const GestureEvent&>(event))); } + if (event.IsPointerEvent()) { + return make_scoped_ptr( + new PointerEvent(static_cast<const PointerEvent&>(event))); + } + if (event.IsScrollEvent()) { return make_scoped_ptr( new ScrollEvent(static_cast<const ScrollEvent&>(event))); @@ -158,6 +206,18 @@ Event::~Event() { ReleaseCopiedNativeEvent(native_event_); } +bool Event::IsMousePointerEvent() const { + return IsPointerEvent() && + AsPointerEvent()->pointer_details().pointer_type == + EventPointerType::POINTER_TYPE_MOUSE; +} + +bool Event::IsTouchPointerEvent() const { + return IsPointerEvent() && + AsPointerEvent()->pointer_details().pointer_type == + EventPointerType::POINTER_TYPE_TOUCH; +} + GestureEvent* Event::AsGestureEvent() { CHECK(IsGestureEvent()); return static_cast<GestureEvent*>(this); @@ -168,6 +228,66 @@ const GestureEvent* Event::AsGestureEvent() const { return static_cast<const GestureEvent*>(this); } +KeyEvent* Event::AsKeyEvent() { + CHECK(IsKeyEvent()); + return static_cast<KeyEvent*>(this); +} + +const KeyEvent* Event::AsKeyEvent() const { + CHECK(IsKeyEvent()); + return static_cast<const KeyEvent*>(this); +} + +MouseEvent* Event::AsMouseEvent() { + CHECK(IsMouseEvent()); + return static_cast<MouseEvent*>(this); +} + +const MouseEvent* Event::AsMouseEvent() const { + CHECK(IsMouseEvent()); + return static_cast<const MouseEvent*>(this); +} + +MouseWheelEvent* Event::AsMouseWheelEvent() { + CHECK(IsMouseWheelEvent()); + return static_cast<MouseWheelEvent*>(this); +} + +const MouseWheelEvent* Event::AsMouseWheelEvent() const { + CHECK(IsMouseWheelEvent()); + return static_cast<const MouseWheelEvent*>(this); +} + +PointerEvent* Event::AsPointerEvent() { + CHECK(IsPointerEvent()); + return static_cast<PointerEvent*>(this); +} + +const PointerEvent* Event::AsPointerEvent() const { + CHECK(IsPointerEvent()); + return static_cast<const PointerEvent*>(this); +} + +ScrollEvent* Event::AsScrollEvent() { + CHECK(IsScrollEvent()); + return static_cast<ScrollEvent*>(this); +} + +const ScrollEvent* Event::AsScrollEvent() const { + CHECK(IsScrollEvent()); + return static_cast<const ScrollEvent*>(this); +} + +TouchEvent* Event::AsTouchEvent() { + CHECK(IsTouchEvent()); + return static_cast<TouchEvent*>(this); +} + +const TouchEvent* Event::AsTouchEvent() const { + CHECK(IsTouchEvent()); + return static_cast<const TouchEvent*>(this); +} + bool Event::HasNativeEvent() const { base::NativeEvent null_event; std::memset(&null_event, 0, sizeof(null_event)); @@ -225,16 +345,17 @@ Event::Event(const base::NativeEvent& native_event, static_cast<base::HistogramBase::Sample>(delta.InMicroseconds()); UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.Browser", delta_sample, 1, 1000000, 100); - std::string name_for_event = - base::StringPrintf("Event.Latency.Browser.%s", name_.c_str()); - base::HistogramBase* counter_for_type = + + // 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", name_.c_str()), + type_, ET_LAST, Add(delta_sample), base::Histogram::FactoryGet( - name_for_event, - 1, - 1000000, - 100, - base::HistogramBase::kUmaTargetedHistogramFlag); - counter_for_type->Add(delta_sample); + base::StringPrintf("Event.Latency.Browser.%s", name_.c_str()), + 1, 1000000, 100, + base::HistogramBase::kUmaTargetedHistogramFlag)); #if defined(USE_X11) if (native_event->type == GenericEvent) { @@ -524,12 +645,7 @@ TouchEvent::TouchEvent(const base::NativeEvent& native_event) rotation_angle_(GetTouchAngle(native_event)), may_cause_scrolling_(false), should_remove_native_touch_id_mapping_(false), - pointer_details_(PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, - GetTouchRadiusX(native_event), - GetTouchRadiusY(native_event), - GetTouchForce(native_event), - /* tilt_x */ 0.0f, - /* tilt_y */ 0.0f)) { + pointer_details_(GetTouchPointerDetailsFromNative(native_event)) { latency()->AddLatencyNumberWithTimestamp( INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, base::TimeTicks::FromInternalValue(time_stamp().ToInternalValue()), 1); @@ -618,9 +734,9 @@ void TouchEvent::UpdateForRootTransform( bool success = gfx::DecomposeTransform(&decomp, inverted_root_transform); DCHECK(success); if (decomp.scale[0]) - pointer_details_.radius_x_ *= decomp.scale[0]; + pointer_details_.radius_x *= decomp.scale[0]; if (decomp.scale[1]) - pointer_details_.radius_y_ *= decomp.scale[1]; + pointer_details_.radius_y *= decomp.scale[1]; } void TouchEvent::DisableSynchronousHandling() { @@ -637,6 +753,108 @@ void TouchEvent::FixRotationAngle() { } //////////////////////////////////////////////////////////////////////////////// +// PointerEvent + +bool PointerEvent::CanConvertFrom(const Event& event) { + switch (event.type()) { + case ET_MOUSE_PRESSED: + case ET_MOUSE_DRAGGED: + case ET_MOUSE_MOVED: + case ET_MOUSE_ENTERED: + case ET_MOUSE_EXITED: + case ET_MOUSE_RELEASED: + case ET_TOUCH_PRESSED: + case ET_TOUCH_MOVED: + case ET_TOUCH_RELEASED: + case ET_TOUCH_CANCELLED: + return true; + default: + return false; + } +} + +PointerEvent::PointerEvent(const PointerEvent& pointer_event) + : LocatedEvent(pointer_event), + pointer_id_(pointer_event.pointer_id()), + details_(pointer_event.pointer_details()) {} + +PointerEvent::PointerEvent(const MouseEvent& mouse_event) + : LocatedEvent(mouse_event), + pointer_id_(kMousePointerId), + details_(mouse_event.pointer_details()) { + DCHECK(CanConvertFrom(mouse_event)); + switch (mouse_event.type()) { + case ET_MOUSE_PRESSED: + SetType(ET_POINTER_DOWN); + break; + + case ET_MOUSE_DRAGGED: + case ET_MOUSE_MOVED: + SetType(ET_POINTER_MOVED); + break; + + case ET_MOUSE_ENTERED: + SetType(ET_POINTER_ENTERED); + break; + + case ET_MOUSE_EXITED: + SetType(ET_POINTER_EXITED); + break; + + case ET_MOUSE_RELEASED: + SetType(ET_POINTER_UP); + break; + + default: + NOTREACHED(); + } +} + +PointerEvent::PointerEvent(const TouchEvent& touch_event) + : LocatedEvent(touch_event), + pointer_id_(touch_event.touch_id()), + details_(touch_event.pointer_details()) { + DCHECK(CanConvertFrom(touch_event)); + switch (touch_event.type()) { + case ET_TOUCH_PRESSED: + SetType(ET_POINTER_DOWN); + break; + + case ET_TOUCH_MOVED: + SetType(ET_POINTER_MOVED); + break; + + case ET_TOUCH_RELEASED: + SetType(ET_POINTER_UP); + break; + + case ET_TOUCH_CANCELLED: + SetType(ET_POINTER_CANCELLED); + break; + + default: + NOTREACHED(); + } +} + +PointerEvent::PointerEvent(EventType type, + EventPointerType pointer_type, + const gfx::Point& location, + const gfx::Point& root_location, + int flags, + int pointer_id, + base::TimeDelta time_stamp) + : LocatedEvent(type, + gfx::PointF(location), + gfx::PointF(location), + time_stamp, + flags), + pointer_id_(pointer_id), + details_(PointerDetails(pointer_type)) {} + +const int PointerEvent::kMousePointerId = std::numeric_limits<int32_t>::max(); + +//////////////////////////////////////////////////////////////////////////////// // KeyEvent // static @@ -680,6 +898,9 @@ bool KeyEvent::IsRepeated(const KeyEvent& event) { return false; } +KeyEvent::KeyEvent(EventType type, base::TimeDelta time_stamp, int flags) + : Event(type, time_stamp, flags) {} + KeyEvent::KeyEvent(const base::NativeEvent& native_event) : Event(native_event, EventTypeFromNative(native_event), @@ -697,6 +918,8 @@ KeyEvent::KeyEvent(const base::NativeEvent& native_event) // Only Windows has native character events. if (is_char_) key_ = DomKey::FromCharacter(native_event.wParam); + else + key_ = PlatformKeyMap::DomKeyFromNative(native_event); #endif } @@ -832,14 +1055,10 @@ base::char16 KeyEvent::GetCharacter() const { // For a control character, key_ contains the corresponding printable // character. To preserve existing behaviour for now, return the control // character here; this will likely change -- see e.g. crbug.com/471488. - if (ucs2_character >= 0x40 && ucs2_character <= 0x7A) + if (ucs2_character >= 0x20 && ucs2_character <= 0x7E) return ucs2_character & 0x1F; if (ucs2_character == '\r') return '\n'; - // Transitionally, if key_ contains another control character, return it. - if (ucs2_character >= 0 && ucs2_character <= 0x1F) - return ucs2_character; - return 0; } return ucs2_character; } diff --git a/chromium/ui/events/event.h b/chromium/ui/events/event.h index dd3ce7fd6bd..49be333ba8e 100644 --- a/chromium/ui/events/event.h +++ b/chromium/ui/events/event.h @@ -6,7 +6,6 @@ #define UI_EVENTS_EVENT_H_ #include <stdint.h> - #include "base/compiler_specific.h" #include "base/event_types.h" #include "base/gtest_prod_util.h" @@ -27,9 +26,23 @@ namespace gfx { class Transform; } +namespace IPC { +template <class P> struct ParamTraits; +} + namespace ui { class EventTarget; +class KeyEvent; +class MouseEvent; +class MouseWheelEvent; +class PointerEvent; +class ScrollEvent; +class TouchEvent; enum class DomCode; +class Event; +class MouseWheelEvent; + +using ScopedEvent = scoped_ptr<Event>; class EVENTS_EXPORT Event { public: @@ -116,6 +129,17 @@ class EVENTS_EXPORT Event { type_ == ET_TOUCH_CANCELLED; } + bool IsPointerEvent() const { + return type_ == ET_POINTER_DOWN || type_ == ET_POINTER_MOVED || + type_ == ET_POINTER_UP || type_ == ET_POINTER_CANCELLED || + type_ == ET_POINTER_ENTERED || type_ == ET_POINTER_EXITED; + } + + // Convenience methods to check pointer type of |this|. Returns false if + // |this| is not a PointerEvent. + bool IsMousePointerEvent() const; + bool IsTouchPointerEvent() const; + bool IsGestureEvent() const { switch (type_) { case ET_GESTURE_SCROLL_BEGIN: @@ -192,7 +216,7 @@ class EVENTS_EXPORT Event { bool IsLocatedEvent() const { return IsMouseEvent() || IsScrollEvent() || IsTouchEvent() || - IsGestureEvent(); + IsGestureEvent() || IsPointerEvent(); } // Convenience methods to cast |this| to a GestureEvent. IsGestureEvent() @@ -200,6 +224,37 @@ class EVENTS_EXPORT Event { GestureEvent* AsGestureEvent(); const GestureEvent* AsGestureEvent() const; + // Convenience methods to cast |this| to a KeyEvent. IsKeyEvent() + // must be true as a precondition to calling these methods. + KeyEvent* AsKeyEvent(); + const KeyEvent* AsKeyEvent() const; + + // Convenience methods to cast |this| to a MouseEvent. IsMouseEvent() + // must be true as a precondition to calling these methods. + MouseEvent* AsMouseEvent(); + const MouseEvent* AsMouseEvent() const; + + // Convenience methods to cast |this| to a MouseWheelEvent. + // IsMouseWheelEvent() must be true as a precondition to calling these + // methods. + MouseWheelEvent* AsMouseWheelEvent(); + const MouseWheelEvent* AsMouseWheelEvent() const; + + // Convenience methods to cast |this| to a PointerEvent. IsPointerEvent() + // must be true as a precondition to calling these methods. + PointerEvent* AsPointerEvent(); + const PointerEvent* AsPointerEvent() const; + + // Convenience methods to cast |this| to a ScrollEvent. IsScrollEvent() + // must be true as a precondition to calling these methods. + ScrollEvent* AsScrollEvent(); + const ScrollEvent* AsScrollEvent() const; + + // Convenience methods to cast |this| to a TouchEvent. IsTouchEvent() + // must be true as a precondition to calling these methods. + TouchEvent* AsTouchEvent(); + const TouchEvent* AsTouchEvent() const; + // Returns true if the event has a valid |native_event_|. bool HasNativeEvent() const; @@ -234,6 +289,9 @@ class EVENTS_EXPORT Event { private: friend class EventTestApi; + // For (de)serialization. + friend struct IPC::ParamTraits<ui::ScopedEvent>; + EventType type_; std::string name_; base::TimeDelta time_stamp_; @@ -255,6 +313,13 @@ class EVENTS_EXPORT CancelModeEvent : public Event { public: CancelModeEvent(); ~CancelModeEvent() override; + + private: + // For (de)serialization. + CancelModeEvent(EventType type, base::TimeDelta time_stamp, int flags) + : Event(type, time_stamp, flags) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::CancelModeEvent>; }; class EVENTS_EXPORT LocatedEvent : public Event { @@ -298,6 +363,13 @@ class EVENTS_EXPORT LocatedEvent : public Event { protected: friend class LocatedEventTestApi; + + // For (de)serialization. + LocatedEvent(EventType type, base::TimeDelta time_stamp, int flags) + : Event(type, time_stamp, flags) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::LocatedEvent>; + explicit LocatedEvent(const base::NativeEvent& native_event); // Create a new LocatedEvent which is identical to the provided model. @@ -327,56 +399,58 @@ class EVENTS_EXPORT LocatedEvent : public Event { // Structure for handling common fields between touch and mouse to support // PointerEvents API. -class EVENTS_EXPORT PointerDetails { +struct EVENTS_EXPORT PointerDetails { public: PointerDetails() {} explicit PointerDetails(EventPointerType pointer_type) - : pointer_type_(pointer_type) {} + : pointer_type(pointer_type), + force(std::numeric_limits<float>::quiet_NaN()) {} PointerDetails(EventPointerType pointer_type, float radius_x, float radius_y, float force, float tilt_x, float tilt_y) - : pointer_type_(pointer_type), - radius_x_(radius_x), - radius_y_(radius_y), - force_(force), - tilt_x_(tilt_x), - tilt_y_(tilt_y) {} - - EventPointerType pointer_type() const { return pointer_type_; }; - - // If we aren't provided with a radius on one axis, use the - // information from the other axis. - float radius_x() const { return radius_x_ > 0 ? radius_x_ : radius_y_; } - float radius_y() const { return radius_y_ > 0 ? radius_y_ : radius_x_; } - float force() const { return force_; } - float tilt_x() const { return tilt_x_; } - float tilt_y() const { return tilt_y_; } + : pointer_type(pointer_type), + // If we aren't provided with a radius on one axis, use the + // information from the other axis. + radius_x(radius_x > 0 ? radius_x : radius_y), + radius_y(radius_y > 0 ? radius_y : radius_x), + force(force), + tilt_x(tilt_x), + tilt_y(tilt_y) {} + + bool operator==(const PointerDetails& other) const { + return pointer_type == other.pointer_type && + radius_x == other.radius_x && + radius_y == other.radius_y && + (force == other.force || + (std::isnan(force) && std::isnan(other.force))) && + tilt_x == other.tilt_x && + tilt_y == other.tilt_y; + } - private: - // For the mutators of the members on this class. - friend class TouchEvent; - friend class MouseEvent; + // For serialization. + friend struct IPC::ParamTraits<ui::PointerDetails>; // The type of pointer device. - EventPointerType pointer_type_ = EventPointerType::POINTER_TYPE_UNKNOWN; + EventPointerType pointer_type = EventPointerType::POINTER_TYPE_UNKNOWN; // Radius of the X (major) axis of the touch ellipse. 0.0 if unknown. - float radius_x_ = 0.0; + float radius_x = 0.0; // Radius of the Y (minor) axis of the touch ellipse. 0.0 if unknown. - float radius_y_ = 0.0; - - // Force (pressure) of the touch. Normalized to be [0, 1]. Default to be 0.0. - float force_ = 0.0; + float radius_y = 0.0; - // Angle of tilt of the X (major) axis. 0.0 if unknown. - float tilt_x_ = 0.0; + // Force (pressure) of the touch. Normalized to be [0, 1] except NaN means + // pressure is not supported by the input device. + float force = 0.0; - // Angle of tilt of the Y (minor) axis. 0.0 if unknown. - float tilt_y_ = 0.0; + // Tilt of a pen/stylus from surface normal as plane angle in degrees, values + // lie in [-90,90]. A positive tilt_x is to the right and a positive tilt_y + // is towards the user. 0.0 if unknown. + float tilt_x = 0.0; + float tilt_y = 0.0; }; class EVENTS_EXPORT MouseEvent : public LocatedEvent { @@ -478,6 +552,13 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { pointer_details_ = details; } + protected: + // For (de)serialization. + MouseEvent(EventType type, base::TimeDelta time_stamp, int flags) + : LocatedEvent(type, time_stamp, flags) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::MouseEvent>; + private: FRIEND_TEST_ALL_PREFIXES(EventTest, DoubleClickRequiresRelease); FRIEND_TEST_ALL_PREFIXES(EventTest, SingleClickRightLeft); @@ -538,6 +619,12 @@ class EVENTS_EXPORT MouseWheelEvent : public MouseEvent { const gfx::Vector2d& offset() const { return offset_; } private: + // For (de)serialization. + MouseWheelEvent(EventType type, base::TimeDelta time_stamp, int flags) + : MouseEvent(type, time_stamp, flags) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::MouseWheelEvent>; + gfx::Vector2d offset_; }; @@ -587,11 +674,6 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { void set_may_cause_scrolling(bool causes) { may_cause_scrolling_ = causes; } bool may_cause_scrolling() const { return may_cause_scrolling_; } - // TODO(robert.bradford): ozone_platform_egltest.cc could use - // UpdateForRootTransform() instead: crbug.com/519337 - void set_radius_x(const float r) { pointer_details_.radius_x_ = r; } - void set_radius_y(const float r) { pointer_details_.radius_y_ = r; } - void set_should_remove_native_touch_id_mapping( bool should_remove_native_touch_id_mapping) { should_remove_native_touch_id_mapping_ = @@ -610,17 +692,27 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { // Event details common to MouseEvent and TouchEvent. const PointerDetails& pointer_details() const { return pointer_details_; } + void set_pointer_details(const PointerDetails& pointer_details) { + pointer_details_ = pointer_details; + } private: + // For (de)serialization. + TouchEvent(EventType type, base::TimeDelta time_stamp, int flags) + : LocatedEvent(type, time_stamp, flags), + should_remove_native_touch_id_mapping_(false) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::TouchEvent>; + // Adjusts rotation_angle_ to within the acceptable range. void FixRotationAngle(); // The identity (typically finger) of the touch starting at 0 and incrementing // for each separable additional touch that the hardware can detect. - const int touch_id_; + int touch_id_; // A unique identifier for the touch event. - const uint32_t unique_event_id_; + uint32_t unique_event_id_; // Clockwise angle (in degrees) of the major axis from the X axis. Must be // less than 180 and non-negative. @@ -641,6 +733,42 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { PointerDetails pointer_details_; }; +class EVENTS_EXPORT PointerEvent : public LocatedEvent { + public: + static const int32_t kMousePointerId; + + // Returns true if a PointerEvent can be constructed from the given mouse or + // touch event. For example, PointerEvent does not support ET_MOUSEWHEEL or + // ET_MOUSE_CAPTURE_CHANGED. + static bool CanConvertFrom(const Event& event); + + PointerEvent(const PointerEvent& pointer_event); + explicit PointerEvent(const MouseEvent& mouse_event); + explicit PointerEvent(const TouchEvent& touch_event); + + PointerEvent(EventType type, + EventPointerType pointer_type, + const gfx::Point& location, + const gfx::Point& root_location, + int flags, + int pointer_id, + base::TimeDelta time_stamp); + + int32_t pointer_id() const { return pointer_id_; } + const PointerDetails& pointer_details() const { return details_; } + + protected: + // For (de)serialization. + PointerEvent(EventType type, base::TimeDelta time_stamp, int flags) + : LocatedEvent(type, time_stamp, flags) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::PointerEvent>; + + private: + int32_t pointer_id_; + PointerDetails details_; +}; + // An interface that individual platforms can use to store additional data on // KeyEvent. // @@ -806,6 +934,11 @@ class EVENTS_EXPORT KeyEvent : public Event { void set_is_char(bool is_char) { is_char_ = is_char; } private: + // For (de)serialization. + KeyEvent(EventType type, base::TimeDelta time_stamp, int flags); + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::KeyEvent>; + // Determine key_ on a keystroke event from code_ and flags(). void ApplyLayout() const; @@ -884,6 +1017,12 @@ class EVENTS_EXPORT ScrollEvent : public MouseEvent { int finger_count() const { return finger_count_; } private: + // For (de)serialization. + ScrollEvent(EventType type, base::TimeDelta time_stamp, int flags) + : MouseEvent(type, time_stamp, flags) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::ScrollEvent>; + // Potential accelerated offsets. float x_offset_; float y_offset_; @@ -916,6 +1055,12 @@ class EVENTS_EXPORT GestureEvent : public LocatedEvent { const GestureEventDetails& details() const { return details_; } private: + // For (de)serialization. + GestureEvent(EventType type, base::TimeDelta time_stamp, int flags) + : LocatedEvent(type, time_stamp, flags) {} + friend struct IPC::ParamTraits<ui::ScopedEvent>; + friend struct IPC::ParamTraits<ui::GestureEvent>; + GestureEventDetails details_; }; diff --git a/chromium/ui/events/event_constants.h b/chromium/ui/events/event_constants.h index 623f5a5c885..a6ef9dbcce5 100644 --- a/chromium/ui/events/event_constants.h +++ b/chromium/ui/events/event_constants.h @@ -26,6 +26,14 @@ enum EventType { ET_TOUCH_CANCELLED, ET_DROP_TARGET_EVENT, + // PointerEvent types + ET_POINTER_DOWN, + ET_POINTER_MOVED, + ET_POINTER_UP, + ET_POINTER_CANCELLED, + ET_POINTER_ENTERED, + ET_POINTER_EXITED, + // GestureEvent types ET_GESTURE_SCROLL_BEGIN, ET_GESTURE_TYPE_START = ET_GESTURE_SCROLL_BEGIN, @@ -34,7 +42,7 @@ enum EventType { ET_GESTURE_TAP, ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_CANCEL, - ET_GESTURE_TAP_UNCONFIRMED, // User tapped, but the tap delay hasn't expired. + ET_GESTURE_TAP_UNCONFIRMED, // User tapped, but the tap delay hasn't expired. ET_GESTURE_DOUBLE_TAP, ET_GESTURE_BEGIN, // The first event sent when each finger is pressed. ET_GESTURE_END, // Sent for each released finger. diff --git a/chromium/ui/events/event_unittest.cc b/chromium/ui/events/event_unittest.cc index 1e9eebbeeb7..3cb6c478a2b 100644 --- a/chromium/ui/events/event_unittest.cc +++ b/chromium/ui/events/event_unittest.cc @@ -18,7 +18,7 @@ #if defined(USE_X11) #include <X11/Xlib.h> #include "ui/events/test/events_test_utils_x11.h" -#include "ui/gfx/x/x11_types.h" +#include "ui/gfx/x/x11_types.h" // nogncheck #endif namespace ui { @@ -60,6 +60,13 @@ TEST(EventTest, GetCharacter) { KeyEvent keyev4(event); EXPECT_EQ(13, keyev4.GetCharacter()); #endif + + // Check if expected Unicode character was returned for a key combination + // contains Control. + // e.g. Control+Shift+2 produces U+200C on "Persian" keyboard. + // http://crbug.com/582453 + KeyEvent keyev5(0x200C, VKEY_UNKNOWN, EF_CONTROL_DOWN | EF_SHIFT_DOWN); + EXPECT_EQ(0x200C, keyev5.GetCharacter()); } TEST(EventTest, ClickCount) { @@ -201,60 +208,60 @@ TEST(EventTest, KeyEvent) { { VKEY_Z, EF_CONTROL_DOWN, '\x1A' }, { VKEY_Z, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1A' }, - { VKEY_2, EF_CONTROL_DOWN, '\0' }, + { VKEY_2, EF_CONTROL_DOWN, '\x12' }, { VKEY_2, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\0' }, - { VKEY_6, EF_CONTROL_DOWN, '\0' }, + { VKEY_6, EF_CONTROL_DOWN, '\x16' }, { VKEY_6, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1E' }, - { VKEY_OEM_MINUS, EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_MINUS, EF_CONTROL_DOWN, '\x0D' }, { VKEY_OEM_MINUS, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1F' }, { VKEY_OEM_4, EF_CONTROL_DOWN, '\x1B' }, - { VKEY_OEM_4, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_4, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1B' }, { VKEY_OEM_5, EF_CONTROL_DOWN, '\x1C' }, - { VKEY_OEM_5, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_5, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1C' }, { VKEY_OEM_6, EF_CONTROL_DOWN, '\x1D' }, - { VKEY_OEM_6, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_6, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1D' }, { VKEY_RETURN, EF_CONTROL_DOWN, '\x0A' }, { VKEY_0, 0, '0' }, { VKEY_0, EF_SHIFT_DOWN, ')' }, { VKEY_0, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON, ')' }, - { VKEY_0, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\0' }, + { VKEY_0, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x09' }, { VKEY_9, 0, '9' }, { VKEY_9, EF_SHIFT_DOWN, '(' }, { VKEY_9, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON, '(' }, - { VKEY_9, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\0' }, + { VKEY_9, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x08' }, - { VKEY_NUMPAD0, EF_CONTROL_DOWN, '\0' }, + { VKEY_NUMPAD0, EF_CONTROL_DOWN, '\x10' }, { VKEY_NUMPAD0, EF_SHIFT_DOWN, '0' }, - { VKEY_NUMPAD9, EF_CONTROL_DOWN, '\0' }, + { VKEY_NUMPAD9, EF_CONTROL_DOWN, '\x19' }, { VKEY_NUMPAD9, EF_SHIFT_DOWN, '9' }, { VKEY_TAB, EF_NONE, '\t' }, { VKEY_TAB, EF_CONTROL_DOWN, '\t' }, { VKEY_TAB, EF_SHIFT_DOWN, '\t' }, - { VKEY_MULTIPLY, EF_CONTROL_DOWN, '\0' }, + { VKEY_MULTIPLY, EF_CONTROL_DOWN, '\x0A' }, { VKEY_MULTIPLY, EF_SHIFT_DOWN, '*' }, - { VKEY_ADD, EF_CONTROL_DOWN, '\0' }, + { VKEY_ADD, EF_CONTROL_DOWN, '\x0B' }, { VKEY_ADD, EF_SHIFT_DOWN, '+' }, - { VKEY_SUBTRACT, EF_CONTROL_DOWN, '\0' }, + { VKEY_SUBTRACT, EF_CONTROL_DOWN, '\x0D' }, { VKEY_SUBTRACT, EF_SHIFT_DOWN, '-' }, - { VKEY_DECIMAL, EF_CONTROL_DOWN, '\0' }, + { VKEY_DECIMAL, EF_CONTROL_DOWN, '\x0E' }, { VKEY_DECIMAL, EF_SHIFT_DOWN, '.' }, - { VKEY_DIVIDE, EF_CONTROL_DOWN, '\0' }, + { VKEY_DIVIDE, EF_CONTROL_DOWN, '\x0F' }, { VKEY_DIVIDE, EF_SHIFT_DOWN, '/' }, - { VKEY_OEM_1, EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_1, EF_CONTROL_DOWN, '\x1B' }, { VKEY_OEM_1, EF_SHIFT_DOWN, ':' }, - { VKEY_OEM_PLUS, EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_PLUS, EF_CONTROL_DOWN, '\x1D' }, { VKEY_OEM_PLUS, EF_SHIFT_DOWN, '+' }, - { VKEY_OEM_COMMA, EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_COMMA, EF_CONTROL_DOWN, '\x0C' }, { VKEY_OEM_COMMA, EF_SHIFT_DOWN, '<' }, - { VKEY_OEM_PERIOD, EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_PERIOD, EF_CONTROL_DOWN, '\x0E' }, { VKEY_OEM_PERIOD, EF_SHIFT_DOWN, '>' }, - { VKEY_OEM_3, EF_CONTROL_DOWN, '\0' }, + { VKEY_OEM_3, EF_CONTROL_DOWN, '\x0' }, { VKEY_OEM_3, EF_SHIFT_DOWN, '~' }, }; @@ -560,13 +567,13 @@ TEST(EventTest, TouchEventRadiusDefaultsToOtherAxis) { TouchEvent event1(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), 0, 0, time, non_zero_length1, 0, 0, 0); - EXPECT_EQ(non_zero_length1, event1.pointer_details().radius_x()); - EXPECT_EQ(non_zero_length1, event1.pointer_details().radius_y()); + EXPECT_EQ(non_zero_length1, event1.pointer_details().radius_x); + EXPECT_EQ(non_zero_length1, event1.pointer_details().radius_y); TouchEvent event2(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), 0, 0, time, 0, non_zero_length2, 0, 0); - EXPECT_EQ(non_zero_length2, event2.pointer_details().radius_x()); - EXPECT_EQ(non_zero_length2, event2.pointer_details().radius_y()); + EXPECT_EQ(non_zero_length2, event2.pointer_details().radius_x); + EXPECT_EQ(non_zero_length2, event2.pointer_details().radius_y); } TEST(EventTest, TouchEventRotationAngleFixing) { @@ -617,63 +624,52 @@ TEST(EventTest, TouchEventRotationAngleFixing) { } } -TEST(EventTest, PointerEventDetailsTouch) { +TEST(EventTest, PointerDetailsTouch) { ui::TouchEvent touch_event_plain(ET_TOUCH_PRESSED, gfx::Point(0, 0), 0, ui::EventTimeForNow()); EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, - touch_event_plain.pointer_details().pointer_type()); - EXPECT_EQ(0.0f, touch_event_plain.pointer_details().radius_x()); - EXPECT_EQ(0.0f, touch_event_plain.pointer_details().radius_y()); - EXPECT_EQ(0.0f, touch_event_plain.pointer_details().force()); - EXPECT_EQ(0.0f, touch_event_plain.pointer_details().tilt_x()); - EXPECT_EQ(0.0f, touch_event_plain.pointer_details().tilt_y()); + touch_event_plain.pointer_details().pointer_type); + EXPECT_EQ(0.0f, touch_event_plain.pointer_details().radius_x); + EXPECT_EQ(0.0f, touch_event_plain.pointer_details().radius_y); + EXPECT_TRUE(std::isnan(touch_event_plain.pointer_details().force)); + EXPECT_EQ(0.0f, touch_event_plain.pointer_details().tilt_x); + EXPECT_EQ(0.0f, touch_event_plain.pointer_details().tilt_y); ui::TouchEvent touch_event_with_details(ET_TOUCH_PRESSED, gfx::Point(0, 0), 0, 0, ui::EventTimeForNow(), 10.0f, 5.0f, 0.0f, 15.0f); EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, - touch_event_with_details.pointer_details().pointer_type()); - EXPECT_EQ(10.0f, touch_event_with_details.pointer_details().radius_x()); - EXPECT_EQ(5.0f, touch_event_with_details.pointer_details().radius_y()); - EXPECT_EQ(15.0f, touch_event_with_details.pointer_details().force()); - EXPECT_EQ(0.0f, touch_event_with_details.pointer_details().tilt_x()); - EXPECT_EQ(0.0f, touch_event_with_details.pointer_details().tilt_y()); + touch_event_with_details.pointer_details().pointer_type); + EXPECT_EQ(10.0f, touch_event_with_details.pointer_details().radius_x); + EXPECT_EQ(5.0f, touch_event_with_details.pointer_details().radius_y); + EXPECT_EQ(15.0f, touch_event_with_details.pointer_details().force); + EXPECT_EQ(0.0f, touch_event_with_details.pointer_details().tilt_x); + EXPECT_EQ(0.0f, touch_event_with_details.pointer_details().tilt_y); ui::TouchEvent touch_event_copy(touch_event_with_details); - EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, - touch_event_copy.pointer_details().pointer_type()); - EXPECT_EQ(10.0f, touch_event_copy.pointer_details().radius_x()); - EXPECT_EQ(5.0f, touch_event_copy.pointer_details().radius_y()); - EXPECT_EQ(15.0f, touch_event_copy.pointer_details().force()); - EXPECT_EQ(0.0f, touch_event_copy.pointer_details().tilt_x()); - EXPECT_EQ(0.0f, touch_event_copy.pointer_details().tilt_y()); + EXPECT_EQ(touch_event_with_details.pointer_details(), + touch_event_copy.pointer_details()); } -TEST(EventTest, PointerEventDetailsMouse) { +TEST(EventTest, PointerDetailsMouse) { ui::MouseEvent mouse_event(ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0); EXPECT_EQ(EventPointerType::POINTER_TYPE_MOUSE, - mouse_event.pointer_details().pointer_type()); - EXPECT_EQ(0.0f, mouse_event.pointer_details().radius_x()); - EXPECT_EQ(0.0f, mouse_event.pointer_details().radius_y()); - EXPECT_EQ(0.0f, mouse_event.pointer_details().force()); - EXPECT_EQ(0.0f, mouse_event.pointer_details().tilt_x()); - EXPECT_EQ(0.0f, mouse_event.pointer_details().tilt_y()); + mouse_event.pointer_details().pointer_type); + EXPECT_EQ(0.0f, mouse_event.pointer_details().radius_x); + EXPECT_EQ(0.0f, mouse_event.pointer_details().radius_y); + EXPECT_TRUE(std::isnan(mouse_event.pointer_details().force)); + EXPECT_EQ(0.0f, mouse_event.pointer_details().tilt_x); + EXPECT_EQ(0.0f, mouse_event.pointer_details().tilt_y); ui::MouseEvent mouse_event_copy(mouse_event); - EXPECT_EQ(EventPointerType::POINTER_TYPE_MOUSE, - mouse_event_copy.pointer_details().pointer_type()); - EXPECT_EQ(0.0f, mouse_event_copy.pointer_details().radius_x()); - EXPECT_EQ(0.0f, mouse_event_copy.pointer_details().radius_y()); - EXPECT_EQ(0.0f, mouse_event_copy.pointer_details().force()); - EXPECT_EQ(0.0f, mouse_event_copy.pointer_details().tilt_x()); - EXPECT_EQ(0.0f, mouse_event_copy.pointer_details().tilt_y()); + EXPECT_EQ(mouse_event.pointer_details(), mouse_event_copy.pointer_details()); } -TEST(EventTest, PointerEventDetailsStylus) { +TEST(EventTest, PointerDetailsStylus) { ui::MouseEvent stylus_event(ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0); ui::PointerDetails pointer_details(EventPointerType::POINTER_TYPE_PEN, @@ -685,21 +681,203 @@ TEST(EventTest, PointerEventDetailsStylus) { stylus_event.set_pointer_details(pointer_details); EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN, - stylus_event.pointer_details().pointer_type()); - EXPECT_EQ(21.0f, stylus_event.pointer_details().force()); - EXPECT_EQ(45.0f, stylus_event.pointer_details().tilt_x()); - EXPECT_EQ(-45.0f, stylus_event.pointer_details().tilt_y()); - EXPECT_EQ(0.0f, stylus_event.pointer_details().radius_x()); - EXPECT_EQ(0.0f, stylus_event.pointer_details().radius_y()); + stylus_event.pointer_details().pointer_type); + EXPECT_EQ(21.0f, stylus_event.pointer_details().force); + EXPECT_EQ(45.0f, stylus_event.pointer_details().tilt_x); + EXPECT_EQ(-45.0f, stylus_event.pointer_details().tilt_y); + EXPECT_EQ(0.0f, stylus_event.pointer_details().radius_x); + EXPECT_EQ(0.0f, stylus_event.pointer_details().radius_y); ui::MouseEvent stylus_event_copy(stylus_event); + EXPECT_EQ(stylus_event.pointer_details(), + stylus_event_copy.pointer_details()); +} + +TEST(EventTest, PointerDetailsCustomTouch) { + ui::TouchEvent touch_event(ET_TOUCH_PRESSED, gfx::Point(0, 0), 0, + ui::EventTimeForNow()); + + EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, + touch_event.pointer_details().pointer_type); + EXPECT_EQ(0.0f, touch_event.pointer_details().radius_x); + EXPECT_EQ(0.0f, touch_event.pointer_details().radius_y); + EXPECT_TRUE(std::isnan(touch_event.pointer_details().force)); + EXPECT_EQ(0.0f, touch_event.pointer_details().tilt_x); + EXPECT_EQ(0.0f, touch_event.pointer_details().tilt_y); + + ui::PointerDetails pointer_details(EventPointerType::POINTER_TYPE_PEN, + /* radius_x */ 5.0f, + /* radius_y */ 6.0f, + /* force */ 21.0f, + /* tilt_x */ 45.0f, + /* tilt_y */ -45.0f); + touch_event.set_pointer_details(pointer_details); + EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN, - stylus_event_copy.pointer_details().pointer_type()); - EXPECT_EQ(21.0f, stylus_event_copy.pointer_details().force()); - EXPECT_EQ(45.0f, stylus_event_copy.pointer_details().tilt_x()); - EXPECT_EQ(-45.0f, stylus_event_copy.pointer_details().tilt_y()); - EXPECT_EQ(0.0f, stylus_event_copy.pointer_details().radius_x()); - EXPECT_EQ(0.0f, stylus_event_copy.pointer_details().radius_y()); + touch_event.pointer_details().pointer_type); + EXPECT_EQ(21.0f, touch_event.pointer_details().force); + EXPECT_EQ(45.0f, touch_event.pointer_details().tilt_x); + EXPECT_EQ(-45.0f, touch_event.pointer_details().tilt_y); + EXPECT_EQ(5.0f, touch_event.pointer_details().radius_x); + EXPECT_EQ(6.0f, touch_event.pointer_details().radius_y); + + ui::TouchEvent touch_event_copy(touch_event); + EXPECT_EQ(touch_event.pointer_details(), touch_event_copy.pointer_details()); +} + +TEST(EventTest, PointerEventCanConvertFrom) { + const gfx::Point point; + const base::TimeDelta time; + + // Common mouse events can be converted. + const EventType mouse_allowed[] = { + ET_MOUSE_PRESSED, + ET_MOUSE_DRAGGED, + ET_MOUSE_MOVED, + ET_MOUSE_ENTERED, + ET_MOUSE_EXITED, + ET_MOUSE_RELEASED + }; + for (size_t i = 0; i < arraysize(mouse_allowed); i++) { + MouseEvent event(mouse_allowed[i], point, point, time, 0, 0); + EXPECT_TRUE(PointerEvent::CanConvertFrom(event)); + } + + // Common touch events can be converted. + const EventType touch_allowed[] = { + ET_TOUCH_PRESSED, + ET_TOUCH_MOVED, + ET_TOUCH_RELEASED, + ET_TOUCH_CANCELLED + }; + for (size_t i = 0; i < arraysize(touch_allowed); i++) { + TouchEvent event(touch_allowed[i], point, 0, time); + EXPECT_TRUE(PointerEvent::CanConvertFrom(event)); + } + + // Capture changes cannot be converted. + EXPECT_FALSE( + PointerEvent::CanConvertFrom( + MouseEvent(ET_MOUSE_CAPTURE_CHANGED, point, point, time, 0, 0))); + + // Wheel events cannot be converted. + EXPECT_FALSE( + PointerEvent::CanConvertFrom( + MouseWheelEvent(gfx::Vector2d(), point, point, time, 0, 0))); + + // Non-mouse non-touch events cannot be converted. + EXPECT_FALSE( + PointerEvent::CanConvertFrom( + KeyEvent(ET_KEY_PRESSED, VKEY_SPACE, EF_NONE))); +} + +TEST(EventTest, PointerEventType) { + const ui::EventType kMouseTypeMap[][2] = { + {ui::ET_MOUSE_PRESSED, ui::ET_POINTER_DOWN}, + {ui::ET_MOUSE_DRAGGED, ui::ET_POINTER_MOVED}, + {ui::ET_MOUSE_MOVED, ui::ET_POINTER_MOVED}, + {ui::ET_MOUSE_ENTERED, ui::ET_POINTER_ENTERED}, + {ui::ET_MOUSE_EXITED, ui::ET_POINTER_EXITED}, + {ui::ET_MOUSE_RELEASED, ui::ET_POINTER_UP}, + }; + const ui::EventType kTouchTypeMap[][2] = { + {ui::ET_TOUCH_PRESSED, ui::ET_POINTER_DOWN}, + {ui::ET_TOUCH_MOVED, ui::ET_POINTER_MOVED}, + {ui::ET_TOUCH_RELEASED, ui::ET_POINTER_UP}, + {ui::ET_TOUCH_CANCELLED, ui::ET_POINTER_CANCELLED}, + }; + + for (size_t i = 0; i < arraysize(kMouseTypeMap); i++) { + ui::MouseEvent mouse_event(kMouseTypeMap[i][0], gfx::Point(0, 0), + gfx::Point(0, 0), base::TimeDelta(), 0, 0); + ui::PointerEvent pointer_event(mouse_event); + EXPECT_EQ(kMouseTypeMap[i][1], pointer_event.type()); + EXPECT_FALSE(pointer_event.IsMouseEvent()); + EXPECT_FALSE(pointer_event.IsTouchEvent()); + EXPECT_TRUE(pointer_event.IsPointerEvent()); + } + + for (size_t i = 0; i < arraysize(kTouchTypeMap); i++) { + ui::TouchEvent touch_event(kTouchTypeMap[i][0], gfx::Point(0, 0), 0, + base::TimeDelta()); + ui::PointerEvent pointer_event(touch_event); + EXPECT_EQ(kTouchTypeMap[i][1], pointer_event.type()); + EXPECT_FALSE(pointer_event.IsMouseEvent()); + EXPECT_FALSE(pointer_event.IsTouchEvent()); + EXPECT_TRUE(pointer_event.IsPointerEvent()); + } +} + +TEST(EventTest, PointerEventId) { + { + ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), + gfx::Point(0, 0), base::TimeDelta(), 0, 0); + ui::PointerEvent pointer_event(mouse_event); + EXPECT_EQ(pointer_event.pointer_id(), ui::PointerEvent::kMousePointerId); + } + + for (int touch_id = 0; touch_id < 8; touch_id++) { + ui::TouchEvent touch_event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), touch_id, + base::TimeDelta()); + ui::PointerEvent pointer_event(touch_event); + EXPECT_EQ(pointer_event.pointer_id(), touch_id); + } +} + +TEST(EventTest, PointerDetailsPointer) { + const float kRadiusX = 10.0f; + const float kRadiusY = 5.0f; + const float kForce = 15.0f; + ui::TouchEvent touch_event(ET_TOUCH_PRESSED, gfx::Point(0, 0), 0, 0, + ui::EventTimeForNow(), kRadiusX, kRadiusY, 0.0f, + kForce); + ui::PointerEvent pointer_event_from_touch(touch_event); + EXPECT_EQ(kRadiusX, pointer_event_from_touch.pointer_details().radius_x); + EXPECT_EQ(kRadiusY, pointer_event_from_touch.pointer_details().radius_y); + EXPECT_EQ(kForce, pointer_event_from_touch.pointer_details().force); + EXPECT_EQ(kRadiusX, pointer_event_from_touch.pointer_details().radius_x); + EXPECT_EQ(0.0f, pointer_event_from_touch.pointer_details().tilt_x); + EXPECT_EQ(0.0f, pointer_event_from_touch.pointer_details().tilt_y); + EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, + pointer_event_from_touch.pointer_details().pointer_type); + + ui::MouseEvent mouse_event(ET_MOUSE_PRESSED, gfx::Point(0, 0), + gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0); + ui::PointerEvent pointer_event_from_mouse(mouse_event); + EXPECT_EQ(mouse_event.pointer_details(), + pointer_event_from_mouse.pointer_details()); +} + +TEST(EventTest, PointerEventClone) { + { + ui::PointerEvent ptr_event( + ui::TouchEvent(ET_TOUCH_PRESSED, gfx::Point(0, 0), 0, 0, + ui::EventTimeForNow(), 10.0f, 5.0f, 0.0f, 15.0f)); + scoped_ptr<ui::Event> clone(ui::Event::Clone(ptr_event)); + EXPECT_TRUE(clone->IsPointerEvent()); + ui::PointerEvent* clone_as_ptr = clone->AsPointerEvent(); + + EXPECT_EQ(ptr_event.type(), clone_as_ptr->type()); + EXPECT_EQ(ptr_event.pointer_id(), clone_as_ptr->pointer_id()); + EXPECT_EQ(ptr_event.pointer_details(), clone_as_ptr->pointer_details()); + EXPECT_EQ(ptr_event.location(), clone_as_ptr->location()); + EXPECT_EQ(ptr_event.root_location(), clone_as_ptr->root_location()); + } + + { + ui::PointerEvent ptr_event( + ui::MouseEvent(ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), + ui::EventTimeForNow(), 0, 0)); + scoped_ptr<ui::Event> clone(ui::Event::Clone(ptr_event)); + EXPECT_TRUE(clone->IsPointerEvent()); + ui::PointerEvent* clone_as_ptr = clone->AsPointerEvent(); + + EXPECT_EQ(ptr_event.type(), clone_as_ptr->type()); + EXPECT_EQ(ptr_event.pointer_id(), clone_as_ptr->pointer_id()); + EXPECT_EQ(ptr_event.pointer_details(), clone_as_ptr->pointer_details()); + EXPECT_EQ(ptr_event.location(), clone_as_ptr->location()); + EXPECT_EQ(ptr_event.root_location(), clone_as_ptr->root_location()); + } } } // namespace ui diff --git a/chromium/ui/events/event_utils.cc b/chromium/ui/events/event_utils.cc index cb4788d1455..aa9ed7b34e9 100644 --- a/chromium/ui/events/event_utils.cc +++ b/chromium/ui/events/event_utils.cc @@ -71,7 +71,7 @@ bool ShouldDefaultToNaturalScroll() { } gfx::Display::TouchSupport GetInternalDisplayTouchSupport() { - gfx::Screen* screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE); + gfx::Screen* screen = gfx::Screen::GetScreen(); // No screen in some unit tests. if (!screen) return gfx::Display::TOUCH_SUPPORT_UNKNOWN; diff --git a/chromium/ui/events/event_utils.h b/chromium/ui/events/event_utils.h index 54081a5cac9..4a8e1637eda 100644 --- a/chromium/ui/events/event_utils.h +++ b/chromium/ui/events/event_utils.h @@ -115,6 +115,10 @@ base::NativeEvent CopyNativeEvent( void ReleaseCopiedNativeEvent( const base::NativeEvent& native_event); +// Returns the detailed pointer information for touch events. +EVENTS_EXPORT PointerDetails +GetTouchPointerDetailsFromNative(const base::NativeEvent& native_event); + // Gets the touch id from a native event. EVENTS_EXPORT int GetTouchId(const base::NativeEvent& native_event); @@ -122,16 +126,9 @@ EVENTS_EXPORT int GetTouchId(const base::NativeEvent& native_event); EVENTS_EXPORT void ClearTouchIdIfReleased( const base::NativeEvent& native_event); -// Gets the radius along the X/Y axis from a native event. Default is 1.0. -EVENTS_EXPORT float GetTouchRadiusX(const base::NativeEvent& native_event); -EVENTS_EXPORT float GetTouchRadiusY(const base::NativeEvent& native_event); - // Gets the angle of the major axis away from the X axis. Default is 0.0. EVENTS_EXPORT float GetTouchAngle(const base::NativeEvent& native_event); -// Gets the force from a native_event. Normalized to be [0, 1]. Default is 0.0. -EVENTS_EXPORT float GetTouchForce(const base::NativeEvent& native_event); - // Gets the fling velocity from a native event. is_cancel is set to true if // this was a tap down, intended to stop an ongoing fling. EVENTS_EXPORT bool GetFlingData(const base::NativeEvent& native_event, diff --git a/chromium/ui/events/events.gyp b/chromium/ui/events/events.gyp index 6c23b30b8b8..c8d994f5e4b 100644 --- a/chromium/ui/events/events.gyp +++ b/chromium/ui/events/events.gyp @@ -140,6 +140,8 @@ 'gestures/gesture_types.h', 'gestures/motion_event_aura.cc', 'gestures/motion_event_aura.h', + 'keycodes/platform_key_map_win.cc', + 'keycodes/platform_key_map_win.h', 'linux/text_edit_command_auralinux.cc', 'linux/text_edit_command_auralinux.h', 'linux/text_edit_key_bindings_delegate_auralinux.cc', @@ -206,9 +208,12 @@ 'android/events_jni_registrar.h', 'android/motion_event_android.cc', 'android/motion_event_android.h', + 'android/key_event_utils.cc', + 'android/key_event_utils.h', ], 'dependencies': [ 'motionevent_jni_headers', + 'keyevent_jni_headers', ], }], ], @@ -310,6 +315,7 @@ 'sources': [ 'ipc/latency_info_param_traits.cc', 'ipc/latency_info_param_traits.h', + 'ipc/latency_info_param_traits_macros.h', ], }, { @@ -383,7 +389,16 @@ }, 'includes': [ '../../build/jar_file_jni_generator.gypi' ], }, + { + 'target_name': 'keyevent_jni_headers', + 'type': 'none', + 'variables': { + 'jni_gen_package': 'ui', + 'input_java_class': 'android/view/KeyEvent.class', + }, + 'includes': [ '../../build/jar_file_jni_generator.gypi' ], + }, ], }], ], -}
\ No newline at end of file +} diff --git a/chromium/ui/events/events_default.cc b/chromium/ui/events/events_default.cc index 7d55e8e790f..b6bf22e7d0d 100644 --- a/chromium/ui/events/events_default.cc +++ b/chromium/ui/events/events_default.cc @@ -96,18 +96,12 @@ int GetTouchId(const base::NativeEvent& native_event) { return event->touch_id(); } -float GetTouchRadiusX(const base::NativeEvent& native_event) { - const ui::TouchEvent* event = - static_cast<const ui::TouchEvent*>(native_event); - DCHECK(event->IsTouchEvent()); - return event->pointer_details().radius_x(); -} - -float GetTouchRadiusY(const base::NativeEvent& native_event) { +PointerDetails GetTouchPointerDetailsFromNative( + const base::NativeEvent& native_event) { const ui::TouchEvent* event = static_cast<const ui::TouchEvent*>(native_event); DCHECK(event->IsTouchEvent()); - return event->pointer_details().radius_y(); + return event->pointer_details(); } float GetTouchAngle(const base::NativeEvent& native_event) { @@ -117,13 +111,6 @@ float GetTouchAngle(const base::NativeEvent& native_event) { return event->rotation_angle(); } -float GetTouchForce(const base::NativeEvent& native_event) { - const ui::TouchEvent* event = - static_cast<const ui::TouchEvent*>(native_event); - DCHECK(event->IsTouchEvent()); - return event->pointer_details().force(); -} - bool GetScrollOffsets(const base::NativeEvent& native_event, float* x_offset, float* y_offset, diff --git a/chromium/ui/events/events_stub.cc b/chromium/ui/events/events_stub.cc index edd7765b16e..a3343110e91 100644 --- a/chromium/ui/events/events_stub.cc +++ b/chromium/ui/events/events_stub.cc @@ -86,24 +86,20 @@ int GetTouchId(const base::NativeEvent& native_event) { return 0; } -float GetTouchRadiusX(const base::NativeEvent& native_event) { - NOTIMPLEMENTED(); - return 0.f; -} - -float GetTouchRadiusY(const base::NativeEvent& native_event) { - NOTIMPLEMENTED(); - return 0.f; -} - float GetTouchAngle(const base::NativeEvent& native_event) { NOTIMPLEMENTED(); return 0.f; } -float GetTouchForce(const base::NativeEvent& native_event) { +PointerDetails GetTouchPointerDetailsFromNative( + const base::NativeEvent& native_event) { NOTIMPLEMENTED(); - return 0.f; + return PointerDetails(EventPointerType::POINTER_TYPE_UNKNOWN, + /* radius_x */ 1.0, + /* radius_y */ 1.0, + /* force */ 0.f, + /* tilt_x */ 0.f, + /* tilt_y */ 0.f); } bool GetScrollOffsets(const base::NativeEvent& native_event, diff --git a/chromium/ui/events/events_unittests.gyp b/chromium/ui/events/events_unittests.gyp index 12f9b2aaa7f..f5a5d01c3e0 100644 --- a/chromium/ui/events/events_unittests.gyp +++ b/chromium/ui/events/events_unittests.gyp @@ -57,9 +57,11 @@ 'ipc/latency_info_param_traits_unittest.cc', 'keycodes/dom/keycode_converter_unittest.cc', 'keycodes/keyboard_code_conversion_unittest.cc', + 'keycodes/platform_key_map_win_unittest.cc', 'latency_info_unittest.cc', 'platform/platform_event_source_unittest.cc', 'scoped_target_handler_unittest.cc', + 'win/event_utils_win_unittest.cc', 'x/events_x_unittest.cc', ], 'include_dirs': [ @@ -103,11 +105,6 @@ 'gestures/motion_event_aura_unittest.cc', ], }], - ['OS=="linux" and use_allocator!="none"', { - 'dependencies': [ - '<(DEPTH)/base/allocator/allocator.gyp:allocator', - ], - }], # Exclude tests that rely on event_utils.h for platforms that do not # provide native cracking, i.e., platforms that use events_stub.cc. ['OS!="win" and use_x11!=1 and use_ozone!=1', { @@ -125,6 +122,7 @@ }], ['OS!="ios"', { 'sources': [ + 'blink/blink_event_util_unittest.cc', 'blink/input_handler_proxy_unittest.cc', 'blink/input_scroll_elasticity_controller_unittest.cc', ], @@ -195,4 +193,4 @@ ], }], ], -}
\ No newline at end of file +} diff --git a/chromium/ui/events/events_unittests_apk.isolate b/chromium/ui/events/events_unittests_apk.isolate index c45db50fe0b..0b33b328f6b 100644 --- a/chromium/ui/events/events_unittests_apk.isolate +++ b/chromium/ui/events/events_unittests_apk.isolate @@ -8,6 +8,7 @@ 'variables': { 'command': [ '<(PRODUCT_DIR)/bin/run_events_unittests', + '--logcat-output-dir', '${ISOLATED_OUTDIR}/logcats', ], 'files': [ '<(PRODUCT_DIR)/bin/run_events_unittests', diff --git a/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc b/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc index e616e7bbd64..be4b197994a 100644 --- a/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc +++ b/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc @@ -6,12 +6,13 @@ #include "base/auto_reset.h" #include "base/logging.h" +#include "ui/events/blink/blink_event_util.h" #include "ui/events/gesture_detection/motion_event.h" namespace ui { FilteredGestureProvider::TouchHandlingResult::TouchHandlingResult() - : succeeded(false), did_generate_scroll(false) { + : succeeded(false), moved_beyond_slop_region(false) { } FilteredGestureProvider::FilteredGestureProvider( @@ -21,7 +22,7 @@ FilteredGestureProvider::FilteredGestureProvider( gesture_provider_(config, this), gesture_filter_(this), handling_event_(false), - last_touch_event_did_generate_scroll_(false) { + any_touch_moved_beyond_slop_region_(false) { } FilteredGestureProvider::TouchHandlingResult @@ -30,7 +31,10 @@ FilteredGestureProvider::OnTouchEvent(const MotionEvent& event) { base::AutoReset<bool> handling_event(&handling_event_, true); pending_gesture_packet_ = GestureEventDataPacket::FromTouch(event); - last_touch_event_did_generate_scroll_ = false; + + if (event.GetAction() == MotionEvent::ACTION_DOWN) + any_touch_moved_beyond_slop_region_ = false; + if (!gesture_provider_.OnTouchEvent(event)) return TouchHandlingResult(); @@ -43,7 +47,7 @@ FilteredGestureProvider::OnTouchEvent(const MotionEvent& event) { TouchHandlingResult result; result.succeeded = true; - result.did_generate_scroll = last_touch_event_did_generate_scroll_; + result.moved_beyond_slop_region = any_touch_moved_beyond_slop_region_; return result; } @@ -76,11 +80,9 @@ const ui::MotionEvent* FilteredGestureProvider::GetCurrentDownEvent() const { void FilteredGestureProvider::OnGestureEvent(const GestureEventData& event) { if (handling_event_) { - if (event.details.type() == ui::ET_GESTURE_SCROLL_BEGIN || - event.details.type() == ui::ET_GESTURE_SCROLL_UPDATE || - event.details.type() == ui::ET_SCROLL_FLING_START) { - last_touch_event_did_generate_scroll_ = true; - } + if (event.details.type() == ui::ET_GESTURE_SCROLL_BEGIN) + any_touch_moved_beyond_slop_region_ = true; + pending_gesture_packet_.Push(event); return; } diff --git a/chromium/ui/events/gesture_detection/filtered_gesture_provider.h b/chromium/ui/events/gesture_detection/filtered_gesture_provider.h index d8a8dc0d252..c0af13b111c 100644 --- a/chromium/ui/events/gesture_detection/filtered_gesture_provider.h +++ b/chromium/ui/events/gesture_detection/filtered_gesture_provider.h @@ -34,11 +34,8 @@ class GESTURE_DETECTION_EXPORT FilteredGestureProvider // |event| and cease further propagation. bool succeeded; - // Whether |event| produced scrolling motion, either the start of a scroll, - // subsequent scroll movement or a fling event. - // TODO(jdduke): Figure out a way to guarantee that this bit propagates with - // the processed touch event as it moves downstream. - bool did_generate_scroll; + // Whether |event| occurred beyond the touch slop region. + bool moved_beyond_slop_region; }; TouchHandlingResult OnTouchEvent(const MotionEvent& event) WARN_UNUSED_RESULT; @@ -66,7 +63,7 @@ class GESTURE_DETECTION_EXPORT FilteredGestureProvider ui::TouchDispositionGestureFilter gesture_filter_; bool handling_event_; - bool last_touch_event_did_generate_scroll_; + bool any_touch_moved_beyond_slop_region_; ui::GestureEventDataPacket pending_gesture_packet_; DISALLOW_COPY_AND_ASSIGN(FilteredGestureProvider); diff --git a/chromium/ui/events/gesture_detection/filtered_gesture_provider_unittest.cc b/chromium/ui/events/gesture_detection/filtered_gesture_provider_unittest.cc index 4514946ccc7..d3e10f637db 100644 --- a/chromium/ui/events/gesture_detection/filtered_gesture_provider_unittest.cc +++ b/chromium/ui/events/gesture_detection/filtered_gesture_provider_unittest.cc @@ -22,63 +22,133 @@ class FilteredGestureProviderTest : public GestureProviderClient, base::MessageLoopForUI message_loop_; }; -TEST_F(FilteredGestureProviderTest, TouchDidGenerateScroll) { +// Single touch drag test: After touch-start, the moved_beyond_slop_region bit +// should stay unset as long as the touch movement is confined to the slop +// region. Once the touch moves beyond the slop region, the bit should remain +// set until (incl) touch-end. +TEST_F(FilteredGestureProviderTest, TouchMovedBeyondSlopRegion_SingleTouch) { GestureProvider::Config config; FilteredGestureProvider provider(config, this); const float kSlopRegion = config.gesture_detector_config.touch_slop; test::MockMotionEvent event; + event.set_event_time(base::TimeTicks::Now()); event.PressPoint(0, 0); auto result = provider.OnTouchEvent(event); EXPECT_TRUE(result.succeeded); - EXPECT_FALSE(result.did_generate_scroll); + EXPECT_FALSE(result.moved_beyond_slop_region); event.MovePoint(0, kSlopRegion / 2.f, 0); result = provider.OnTouchEvent(event); EXPECT_TRUE(result.succeeded); - EXPECT_FALSE(result.did_generate_scroll); + EXPECT_FALSE(result.moved_beyond_slop_region); - // Exceeding the slop should triggering scrolling and be reflected in the API. event.MovePoint(0, kSlopRegion * 2.f, 0); result = provider.OnTouchEvent(event); EXPECT_TRUE(result.succeeded); - EXPECT_TRUE(result.did_generate_scroll); + EXPECT_TRUE(result.moved_beyond_slop_region); - // No movement should indicate no scrolling. event.MovePoint(0, kSlopRegion * 2.f, 0); result = provider.OnTouchEvent(event); EXPECT_TRUE(result.succeeded); - EXPECT_FALSE(result.did_generate_scroll); + EXPECT_TRUE(result.moved_beyond_slop_region); - // Nonzero movement should reflect scrolling after exceeding the slop region. event.MovePoint(0, 0, 0); result = provider.OnTouchEvent(event); EXPECT_TRUE(result.succeeded); - EXPECT_TRUE(result.did_generate_scroll); + EXPECT_TRUE(result.moved_beyond_slop_region); - // Ending a touch with no fling should not indicate scrolling. event.ReleasePoint(); result = provider.OnTouchEvent(event); EXPECT_TRUE(result.succeeded); - EXPECT_FALSE(result.did_generate_scroll); + EXPECT_TRUE(result.moved_beyond_slop_region); - // Ending a touch with a fling *should* indicate scrolling. + // A new touch sequence should reset the bit. base::TimeTicks time = base::TimeTicks::Now(); event.PressPoint(0, 0); event.set_event_time(time); - ASSERT_TRUE(provider.OnTouchEvent(event).succeeded); + result = provider.OnTouchEvent(event); + ASSERT_TRUE(result.succeeded); + EXPECT_FALSE(result.moved_beyond_slop_region); + // A fling should set the bit right away. time += base::TimeDelta::FromMilliseconds(10); event.MovePoint(0, kSlopRegion * 50, 0); event.set_event_time(time); - ASSERT_TRUE(provider.OnTouchEvent(event).succeeded); + result = provider.OnTouchEvent(event); + ASSERT_TRUE(result.succeeded); + EXPECT_TRUE(result.moved_beyond_slop_region); event.ReleasePoint(); result = provider.OnTouchEvent(event); EXPECT_TRUE(result.succeeded); - EXPECT_TRUE(result.did_generate_scroll); + EXPECT_TRUE(result.moved_beyond_slop_region); +} + +// Multi-touch: The moved_beyond_slop_region bit should stay unset as long as +// all touch-points are stationary, and should be set after (including) the +// first movement in any touch-point. +TEST_F(FilteredGestureProviderTest, TouchMovedBeyondSlopRegion_MultiTouch) { + GestureProvider::Config config; + FilteredGestureProvider provider(config, this); + + const float kSlopRegion = config.gesture_detector_config.touch_slop; + + test::MockMotionEvent event; + + float x = 0; + const float y0 = 0; + const float y1 = kSlopRegion * 10.f; + const float y2 = kSlopRegion * 20.f; + + event.set_event_time(base::TimeTicks::Now()); + event.PressPoint(x, y0); + auto result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_FALSE(result.moved_beyond_slop_region); + + event.PressPoint(x, y1); + result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_FALSE(result.moved_beyond_slop_region); + + event.PressPoint(x, y2); + result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_FALSE(result.moved_beyond_slop_region); + + for (float multiplier = 0.5f; multiplier < 3.f; multiplier += 2.f) { + x = kSlopRegion * multiplier; + + event.MovePoint(0, x, y0); + result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_TRUE(result.moved_beyond_slop_region); + + event.MovePoint(0, x, y0); + result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_TRUE(result.moved_beyond_slop_region); + + event.MovePoint(2, x, y2); + result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_TRUE(result.moved_beyond_slop_region); + + event.MovePoint(1, x, y1); + result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_TRUE(result.moved_beyond_slop_region); + } + + for(int i = 0; i < 3; i++) { + event.ReleasePoint(); + result = provider.OnTouchEvent(event); + EXPECT_TRUE(result.succeeded); + EXPECT_TRUE(result.moved_beyond_slop_region); + } } } // namespace ui diff --git a/chromium/ui/events/gesture_detection/gesture_detector.cc b/chromium/ui/events/gesture_detection/gesture_detector.cc index 2996bc70816..6fc39a8e3dd 100644 --- a/chromium/ui/events/gesture_detection/gesture_detector.cc +++ b/chromium/ui/events/gesture_detection/gesture_detector.cc @@ -61,6 +61,8 @@ GestureDetector::Config::Config() single_tap_repeat_interval(1), velocity_tracker_strategy(VelocityTracker::Strategy::STRATEGY_DEFAULT) {} +GestureDetector::Config::Config(const Config& other) = default; + GestureDetector::Config::~Config() {} class GestureDetector::TimeoutGestureHandler { diff --git a/chromium/ui/events/gesture_detection/gesture_detector.h b/chromium/ui/events/gesture_detection/gesture_detector.h index 49def14b735..9fb699985f2 100644 --- a/chromium/ui/events/gesture_detection/gesture_detector.h +++ b/chromium/ui/events/gesture_detection/gesture_detector.h @@ -25,6 +25,7 @@ class GESTURE_DETECTION_EXPORT GestureDetector { public: struct GESTURE_DETECTION_EXPORT Config { Config(); + Config(const Config& other); ~Config(); base::TimeDelta longpress_timeout; diff --git a/chromium/ui/events/gesture_detection/gesture_event_data.cc b/chromium/ui/events/gesture_detection/gesture_event_data.cc index b166ffa0a13..676dadef6f8 100644 --- a/chromium/ui/events/gesture_detection/gesture_event_data.cc +++ b/chromium/ui/events/gesture_detection/gesture_event_data.cc @@ -49,6 +49,8 @@ GestureEventData::GestureEventData(EventType type, details.set_bounding_box(other.details.bounding_box_f()); } +GestureEventData::GestureEventData(const GestureEventData& other) = default; + GestureEventData::GestureEventData() : motion_event_id(0), primary_tool_type(MotionEvent::TOOL_TYPE_UNKNOWN), diff --git a/chromium/ui/events/gesture_detection/gesture_event_data.h b/chromium/ui/events/gesture_detection/gesture_event_data.h index 8f54355dcf5..541fe125f6b 100644 --- a/chromium/ui/events/gesture_detection/gesture_event_data.h +++ b/chromium/ui/events/gesture_detection/gesture_event_data.h @@ -30,6 +30,7 @@ struct GESTURE_DETECTION_EXPORT GestureEventData { const gfx::RectF& bounding_box, int flags); GestureEventData(EventType type, const GestureEventData&); + GestureEventData(const GestureEventData& other); EventType type() const { return details.type(); } diff --git a/chromium/ui/events/gesture_detection/gesture_provider.cc b/chromium/ui/events/gesture_detection/gesture_provider.cc index 2ea2b53407f..40944ff8654 100644 --- a/chromium/ui/events/gesture_detection/gesture_provider.cc +++ b/chromium/ui/events/gesture_detection/gesture_provider.cc @@ -75,6 +75,8 @@ GestureProvider::Config::Config() max_gesture_bounds_length(0) { } +GestureProvider::Config::Config(const Config& other) = default; + GestureProvider::Config::~Config() { } diff --git a/chromium/ui/events/gesture_detection/gesture_provider.h b/chromium/ui/events/gesture_detection/gesture_provider.h index c738f53ab32..0da33163419 100644 --- a/chromium/ui/events/gesture_detection/gesture_provider.h +++ b/chromium/ui/events/gesture_detection/gesture_provider.h @@ -28,6 +28,7 @@ class GESTURE_DETECTION_EXPORT GestureProvider { public: struct GESTURE_DETECTION_EXPORT Config { Config(); + Config(const Config& other); ~Config(); gfx::Display display; GestureDetector::Config gesture_detector_config; diff --git a/chromium/ui/events/gesture_detection/gesture_provider_config_helper.cc b/chromium/ui/events/gesture_detection/gesture_provider_config_helper.cc index 27081026df0..c13636687c9 100644 --- a/chromium/ui/events/gesture_detection/gesture_provider_config_helper.cc +++ b/chromium/ui/events/gesture_detection/gesture_provider_config_helper.cc @@ -90,7 +90,7 @@ GestureProvider::Config GetGestureProviderConfig( break; } - gfx::Screen* screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE); + gfx::Screen* screen = gfx::Screen::GetScreen(); // |screen| is sometimes NULL during tests. if (screen) config.display = screen->GetPrimaryDisplay(); diff --git a/chromium/ui/events/gesture_detection/motion_event_generic.cc b/chromium/ui/events/gesture_detection/motion_event_generic.cc index 041bddda256..935584c4796 100644 --- a/chromium/ui/events/gesture_detection/motion_event_generic.cc +++ b/chromium/ui/events/gesture_detection/motion_event_generic.cc @@ -50,6 +50,8 @@ PointerProperties::PointerProperties(const MotionEvent& event, source_device_id(0) { } +PointerProperties::PointerProperties(const PointerProperties& other) = default; + void PointerProperties::SetAxesAndOrientation(float radius_x, float radius_y, float rotation_angle_degree) { diff --git a/chromium/ui/events/gesture_detection/motion_event_generic.h b/chromium/ui/events/gesture_detection/motion_event_generic.h index c5ea1b00f4c..bc9b351381a 100644 --- a/chromium/ui/events/gesture_detection/motion_event_generic.h +++ b/chromium/ui/events/gesture_detection/motion_event_generic.h @@ -19,6 +19,7 @@ struct GESTURE_DETECTION_EXPORT PointerProperties { PointerProperties(); PointerProperties(float x, float y, float touch_major); PointerProperties(const MotionEvent& event, size_t pointer_index); + PointerProperties(const PointerProperties& other); // Sets |touch_major|, |touch_minor|, and |orientation| from the given radius // and rotation angle (in degrees). diff --git a/chromium/ui/events/gesture_event_details.h b/chromium/ui/events/gesture_event_details.h index ff88041fb3e..6a79a4ad1c1 100644 --- a/chromium/ui/events/gesture_event_details.h +++ b/chromium/ui/events/gesture_event_details.h @@ -5,12 +5,18 @@ #ifndef UI_EVENTS_GESTURE_DETECTION_GESTURE_EVENT_DETAILS_H_ #define UI_EVENTS_GESTURE_DETECTION_GESTURE_EVENT_DETAILS_H_ +#include <string.h> + #include "base/logging.h" #include "ui/events/event_constants.h" #include "ui/events/events_base_export.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" +namespace IPC { +template <class P> struct ParamTraits; +} + namespace ui { struct EVENTS_BASE_EXPORT GestureEventDetails { @@ -137,6 +143,14 @@ struct EVENTS_BASE_EXPORT GestureEventDetails { return data_.scroll_update.previous_update_in_sequence_prevented; } + // Supports comparison over internal structures for testing. + bool operator==(const GestureEventDetails& other) const { + return type_ == other.type_ && + !memcmp(&data_, &other.data_, sizeof(Details)) && + touch_points_ == other.touch_points_ && + bounding_box_ == other.bounding_box_; + } + private: EventType type_; union Details { @@ -182,6 +196,10 @@ struct EVENTS_BASE_EXPORT GestureEventDetails { int tap_count; // TAP repeat count. } data_; + // For mojo native implementation of (de)serialization. + friend struct IPC::ParamTraits<ui::GestureEventDetails>; + friend struct IPC::ParamTraits<ui::GestureEventDetails::Details>; + int touch_points_; // Number of active touch points in the gesture. // Bounding box is an axis-aligned rectangle that contains all the 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 d66e842502d..27eb0b4d01a 100644 --- a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h +++ b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "third_party/WebKit/public/platform/WebGestureCurve.h" diff --git a/chromium/ui/events/gestures/gesture_provider_aura.cc b/chromium/ui/events/gestures/gesture_provider_aura.cc index 9bfe5fd605d..1adbc641244 100644 --- a/chromium/ui/events/gestures/gesture_provider_aura.cc +++ b/chromium/ui/events/gestures/gesture_provider_aura.cc @@ -36,7 +36,7 @@ bool GestureProviderAura::OnTouchEvent(TouchEvent* event) { if (!result.succeeded) return false; - event->set_may_cause_scrolling(result.did_generate_scroll); + event->set_may_cause_scrolling(result.moved_beyond_slop_region); pointer_state_.CleanupRemovedTouchPoints(*event); return true; } diff --git a/chromium/ui/events/gestures/motion_event_aura.cc b/chromium/ui/events/gestures/motion_event_aura.cc index 7b6e34ec117..eff71e1abe2 100644 --- a/chromium/ui/events/gestures/motion_event_aura.cc +++ b/chromium/ui/events/gestures/motion_event_aura.cc @@ -33,11 +33,11 @@ PointerProperties GetPointerPropertiesFromTouchEvent(const TouchEvent& touch) { pointer_properties.raw_x = touch.root_location_f().x(); pointer_properties.raw_y = touch.root_location_f().y(); pointer_properties.id = touch.touch_id(); - pointer_properties.pressure = touch.pointer_details().force(); + pointer_properties.pressure = touch.pointer_details().force; pointer_properties.source_device_id = touch.source_device_id(); - pointer_properties.SetAxesAndOrientation(touch.pointer_details().radius_x(), - touch.pointer_details().radius_y(), + pointer_properties.SetAxesAndOrientation(touch.pointer_details().radius_x, + touch.pointer_details().radius_y, touch.rotation_angle()); if (!pointer_properties.touch_major) { pointer_properties.touch_major = @@ -48,7 +48,7 @@ PointerProperties GetPointerPropertiesFromTouchEvent(const TouchEvent& touch) { } pointer_properties.tool_type = EventPointerTypeToMotionEventToolType( - touch.pointer_details().pointer_type()); + touch.pointer_details().pointer_type); return pointer_properties; } diff --git a/chromium/ui/events/gestures/motion_event_aura_unittest.cc b/chromium/ui/events/gestures/motion_event_aura_unittest.cc index 75f49ff25e6..d9a5f8af125 100644 --- a/chromium/ui/events/gestures/motion_event_aura_unittest.cc +++ b/chromium/ui/events/gestures/motion_event_aura_unittest.cc @@ -426,14 +426,16 @@ TEST(MotionEventAuraTest, Cancel) { TEST(MotionEventAuraTest, ToolType) { MotionEventAura event; - - EXPECT_TRUE(event.OnTouch(TouchWithType(ET_TOUCH_PRESSED, 7))); + TouchEvent touch_event = TouchWithType(ET_TOUCH_PRESSED, 7); + EXPECT_TRUE(event.OnTouch(touch_event)); ASSERT_EQ(1U, event.GetPointerCount()); EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(0)); - // TODO(robert.bradford): crbug.com/575162: Test TOOL_TYPE_PEN when - // TouchEvents can have their PointerDetails::pointer_type() something other - // than POINTER_TYPE_TOUCH + PointerDetails pointer_details(EventPointerType::POINTER_TYPE_PEN, 5.0f, 5.0f, + 1.0f, 0.0f, 0.0f); + touch_event.set_pointer_details(pointer_details); + EXPECT_TRUE(event.OnTouch(touch_event)); + EXPECT_EQ(MotionEvent::TOOL_TYPE_STYLUS, event.GetToolType(0)); } TEST(MotionEventAuraTest, Flags) { diff --git a/chromium/ui/events/ipc/BUILD.gn b/chromium/ui/events/ipc/BUILD.gn index f7eaf887b80..b6ccbf51d5f 100644 --- a/chromium/ui/events/ipc/BUILD.gn +++ b/chromium/ui/events/ipc/BUILD.gn @@ -4,11 +4,12 @@ import("//build/config/ui.gni") -component("events_ipc") { +component("ipc") { output_name = "events_ipc" sources = [ "latency_info_param_traits.cc", "latency_info_param_traits.h", + "latency_info_param_traits_macros.h", ] defines = [ "EVENTS_IPC_IMPLEMENTATION" ] diff --git a/chromium/ui/events/ipc/latency_info_param_traits.cc b/chromium/ui/events/ipc/latency_info_param_traits.cc index 27d9ab6736f..a472307407a 100644 --- a/chromium/ui/events/ipc/latency_info_param_traits.cc +++ b/chromium/ui/events/ipc/latency_info_param_traits.cc @@ -29,23 +29,18 @@ namespace IPC { #include "ui/events/ipc/latency_info_param_traits.h" namespace IPC { -void ParamTraits<ui::LatencyInfo>::Write(Message* m, - const param_type& p) { +void ParamTraits<ui::LatencyInfo>::Write(base::Pickle* m, const param_type& p) { WriteParam(m, p.trace_name_); WriteParam(m, p.latency_components_); WriteParam(m, p.input_coordinates_size_); for (size_t i = 0; i < p.input_coordinates_size_; i++) { WriteParam(m, p.input_coordinates_[i]); } - WriteParam(m, p.coalesced_events_size_); - for (size_t i = 0; i < p.coalesced_events_size_; i++) { - WriteParam(m, p.timestamps_of_coalesced_events_[i]); - } WriteParam(m, p.trace_id_); WriteParam(m, p.terminated_); } -bool ParamTraits<ui::LatencyInfo>::Read(const Message* m, +bool ParamTraits<ui::LatencyInfo>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* p) { if (!ReadParam(m, iter, &p->trace_name_)) @@ -64,17 +59,6 @@ bool ParamTraits<ui::LatencyInfo>::Read(const Message* m, return false; } - double timestamp; - uint32_t coalesced_events_size; - if (!ReadParam(m, iter, &coalesced_events_size)) - return false; - for (size_t i = 0; i < coalesced_events_size; i++) { - if (!ReadParam(m, iter, ×tamp)) - return false; - if (!p->AddCoalescedEventTimestamp(timestamp)) - return false; - } - if (!ReadParam(m, iter, &p->trace_id_)) return false; if (!ReadParam(m, iter, &p->terminated_)) @@ -95,12 +79,6 @@ void ParamTraits<ui::LatencyInfo>::Log(const param_type& p, LogParam(p.input_coordinates_[i], l); l->append(" "); } - LogParam(p.coalesced_events_size_, l); - l->append(" "); - for (size_t i = 0; i < p.coalesced_events_size_; i++) { - LogParam(p.timestamps_of_coalesced_events_[i], l); - l->append(" "); - } LogParam(p.trace_id_, l); l->append(" "); LogParam(p.terminated_, l); diff --git a/chromium/ui/events/ipc/latency_info_param_traits.h b/chromium/ui/events/ipc/latency_info_param_traits.h index 0716f432d9c..9e11104341f 100644 --- a/chromium/ui/events/ipc/latency_info_param_traits.h +++ b/chromium/ui/events/ipc/latency_info_param_traits.h @@ -12,8 +12,10 @@ namespace IPC { template <> struct EVENTS_IPC_EXPORT ParamTraits<ui::LatencyInfo> { typedef ui::LatencyInfo param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* p); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p); static void Log(const param_type& p, std::string* l); }; } // namespace IPC diff --git a/chromium/ui/events/ipc/latency_info_param_traits_unittest.cc b/chromium/ui/events/ipc/latency_info_param_traits_unittest.cc index 12f6a51135b..69224dace99 100644 --- a/chromium/ui/events/ipc/latency_info_param_traits_unittest.cc +++ b/chromium/ui/events/ipc/latency_info_param_traits_unittest.cc @@ -16,7 +16,6 @@ TEST(LatencyInfoParamTraitsTest, Basic) { LatencyInfo latency; ASSERT_FALSE(latency.terminated()); ASSERT_EQ(0u, latency.input_coordinates_size()); - ASSERT_EQ(0u, latency.coalesced_events_size()); latency.AddLatencyNumber(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 1234, 0); latency.AddLatencyNumber(INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 1234, 100); latency.AddLatencyNumber(INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, @@ -29,15 +28,9 @@ TEST(LatencyInfoParamTraitsTest, Basic) { EXPECT_FALSE(latency.AddInputCoordinate( LatencyInfo::InputCoordinate(102, 202))); - EXPECT_TRUE(latency.AddCoalescedEventTimestamp(10.0)); - EXPECT_TRUE(latency.AddCoalescedEventTimestamp(20.0)); - // Up to 2 coalesced events is allowed. - EXPECT_FALSE(latency.AddCoalescedEventTimestamp(30.0)); - EXPECT_EQ(100, latency.trace_id()); EXPECT_TRUE(latency.terminated()); EXPECT_EQ(2u, latency.input_coordinates_size()); - EXPECT_EQ(2u, latency.coalesced_events_size()); IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); IPC::WriteParam(&msg, latency); @@ -54,11 +47,6 @@ TEST(LatencyInfoParamTraitsTest, Basic) { EXPECT_EQ(latency.input_coordinates()[i].y, output.input_coordinates()[i].y); } - EXPECT_EQ(latency.coalesced_events_size(), output.coalesced_events_size()); - for (size_t i = 0; i < latency.coalesced_events_size(); i++) { - EXPECT_EQ(latency.timestamps_of_coalesced_events()[i], - output.timestamps_of_coalesced_events()[i]); - } EXPECT_TRUE(output.FindLatency(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 1234, @@ -80,12 +68,9 @@ TEST(LatencyInfoParamTraitsTest, InvalidData) { IPC::WriteParam(&msg, std::string()); ui::LatencyInfo::LatencyMap components; IPC::WriteParam(&msg, components); + // input_coordinates_size is 2 but only one InputCoordinate is written. IPC::WriteParam(&msg, static_cast<uint32_t>(2)); IPC::WriteParam(&msg, ui::LatencyInfo::InputCoordinate()); - IPC::WriteParam(&msg, ui::LatencyInfo::InputCoordinate()); - // coalesced_events_size is 2 but only one event timestamp is written. - IPC::WriteParam(&msg, static_cast<uint32_t>(2)); - IPC::WriteParam(&msg, static_cast<double>(10.0)); IPC::WriteParam(&msg, static_cast<int64_t>(1234)); IPC::WriteParam(&msg, true); diff --git a/chromium/ui/events/keycodes/dom/dom_key.h b/chromium/ui/events/keycodes/dom/dom_key.h index 9c8847869c6..810f82137f6 100644 --- a/chromium/ui/events/keycodes/dom/dom_key.h +++ b/chromium/ui/events/keycodes/dom/dom_key.h @@ -8,6 +8,7 @@ #include <stdint.h> #include "base/logging.h" +#include "ipc/ipc_param_traits.h" namespace ui { @@ -80,14 +81,10 @@ class DomKey { // included here to create constants for them in the DomKey:: scope. #define DOM_KEY_MAP_DECLARATION enum Key : Base #define DOM_KEY_UNI(key, id, value) id = (TYPE_UNICODE | (value)) -#define DOM_KEY_MAP_BEGIN FIRST_NON_UNICODE = TYPE_NON_UNICODE, -#define DOM_KEY_MAP(key, id) id -#define DOM_KEY_MAP_END LAST_NON_UNICODE +#define DOM_KEY_MAP(key, id, value) id = (TYPE_NON_UNICODE | (value)) #include "ui/events/keycodes/dom/dom_key_data.inc" #undef DOM_KEY_MAP_DECLARATION -#undef DOM_KEY_MAP_BEGIN #undef DOM_KEY_MAP -#undef DOM_KEY_MAP_END #undef DOM_KEY_UNI // Create a DomKey, with the undefined-value sentinel DomKey::NONE. @@ -151,6 +148,8 @@ class DomKey { }; private: + friend struct IPC::ParamTraits<ui::DomKey>; + Base value_; }; diff --git a/chromium/ui/events/keycodes/dom/dom_key_data.inc b/chromium/ui/events/keycodes/dom/dom_key_data.inc index c4a84509dd6..507d97e4bba 100644 --- a/chromium/ui/events/keycodes/dom/dom_key_data.inc +++ b/chromium/ui/events/keycodes/dom/dom_key_data.inc @@ -10,7 +10,8 @@ // (enums) and string tables. These names are defined by: // // [0] DOM Level 3 KeyboardEvent key Values, -// http://www.w3.org/TR/DOM-Level-3-Events-key/ +// http://www.w3.org/TR/uievents-key/ +// (current as of W3C Working Draft 15 December 2015) // // That reference should be consulted on the meaning and usage of these // values; this file does not attempt to describe that. @@ -18,6 +19,9 @@ // The names given in the table here directly match the DOM Level 3 value, // and for the most part the corresponding enum identifier is generated from // the name by converting W3C's CamelCaseStyle to Chromium's C_MACRO_STYLE. +// +// NOTE: Some uses of this data expect that encodings remain fixed, +// so existing key values should not be changed. DOM_KEY_MAP_DECLARATION { @@ -34,361 +38,498 @@ DOM_KEY_MAP_DECLARATION { // DELETE conflicts with an unscoped declaration in Windows' <windows.h>. DOM_KEY_UNI("Delete", DEL, 0x007F), - DOM_KEY_MAP_BEGIN - // ========================================================= // Special Key Values - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-special + // http://www.w3.org/TR/uievents-key/#keys-special // ========================================================= // Unable to identify another key value - DOM_KEY_MAP("Unidentified", UNIDENTIFIED), + DOM_KEY_MAP("Unidentified", UNIDENTIFIED, 0x0001), // ========================================================== // Modifier Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-modifier + // http://www.w3.org/TR/uievents-key/#keys-modifier // ========================================================== - // Key Enum - DOM_KEY_MAP("Accel", ACCEL), // Never returned in a key event - DOM_KEY_MAP("Alt", ALT), - DOM_KEY_MAP("AltGraph", ALT_GRAPH), - DOM_KEY_MAP("CapsLock", CAPS_LOCK), - DOM_KEY_MAP("Control", CONTROL), - DOM_KEY_MAP("Fn", FN), - DOM_KEY_MAP("FnLock", FN_LOCK), - DOM_KEY_MAP("Hyper", HYPER), - DOM_KEY_MAP("Meta", META), - DOM_KEY_MAP("NumLock", NUM_LOCK), - DOM_KEY_MAP("OS", OS), - DOM_KEY_MAP("ScrollLock", SCROLL_LOCK), - DOM_KEY_MAP("Shift", SHIFT), - DOM_KEY_MAP("Super", SUPER), - DOM_KEY_MAP("Symbol", SYMBOL), - DOM_KEY_MAP("SymbolLock", SYMBOL_LOCK), + // Key Enum Value + // "Accel" is never returned in a key event. + DOM_KEY_MAP("Accel", ACCEL, 0x0101), + DOM_KEY_MAP("Alt", ALT, 0x0102), + DOM_KEY_MAP("AltGraph", ALT_GRAPH, 0x0103), + DOM_KEY_MAP("CapsLock", CAPS_LOCK, 0x0104), + DOM_KEY_MAP("Control", CONTROL, 0x0105), + DOM_KEY_MAP("Fn", FN, 0x0106), + DOM_KEY_MAP("FnLock", FN_LOCK, 0x0107), + DOM_KEY_MAP("Hyper", HYPER, 0x0108), + DOM_KEY_MAP("Meta", META, 0x0109), + DOM_KEY_MAP("NumLock", NUM_LOCK, 0x010A), + // "OS" was removed from the spec. + // ("OS", OS, 0x010B), + DOM_KEY_MAP("ScrollLock", SCROLL_LOCK, 0x010C), + DOM_KEY_MAP("Shift", SHIFT, 0x010D), + DOM_KEY_MAP("Super", SUPER, 0x010E), + DOM_KEY_MAP("Symbol", SYMBOL, 0x010F), + DOM_KEY_MAP("SymbolLock", SYMBOL_LOCK, 0x0110), // Non-standard value corresponding to XKB keysym ISO_Level5_Shift, // an additional printable-character modifier like Shift and AltGraph, // used by such layouts as Neo (German) and Multix (French-Canadian). - // Key Enum - DOM_KEY_MAP("ShiftLevel5", SHIFT_LEVEL5), + // Key Enum Value + DOM_KEY_MAP("ShiftLevel5", SHIFT_LEVEL5, 0x0111), - // Non-standard value representing a latching-modifier key in ui::Keyevent. + // Non-standard value representing a latching-modifier key in ui::KeyEvent. // This is present on some keyboard layouts (Latvian, Tibetan, Cameroon), // and applies the modifier to the next (non-modifier) key. // These events are consumed internally and not exposed to the web, // either directly or as synthetic modifier up/down events. - // Key Enum - DOM_KEY_MAP(".AltGraphLatch", ALT_GRAPH_LATCH), + // Key Enum Value + DOM_KEY_MAP(".AltGraphLatch", ALT_GRAPH_LATCH, 0x0112), // ============================================================ // Whitespace Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-whitespace + // http://www.w3.org/TR/uievents-key/#keys-whitespace // ============================================================ - // Key Enum - // "Enter" is encoded in the Unicode space (at the end of this list). - DOM_KEY_MAP("Separator", SEPARATOR), + // Key Enum Value + // "Enter" is encoded in the Unicode space (at the top of this list). + // "Separator" was removed from the spec. + // ("Separator", SEPARATOR, 0x0201), // "Tab" is encoded in the Unicode space. // ============================================================ // Navigation Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-navigation + // http://www.w3.org/TR/uievents-key/#keys-navigation // ============================================================ - // Key Enum - DOM_KEY_MAP("ArrowDown", ARROW_DOWN), - DOM_KEY_MAP("ArrowLeft", ARROW_LEFT), - DOM_KEY_MAP("ArrowRight", ARROW_RIGHT), - DOM_KEY_MAP("ArrowUp", ARROW_UP), - DOM_KEY_MAP("End", END), - DOM_KEY_MAP("Home", HOME), - DOM_KEY_MAP("PageDown", PAGE_DOWN), - DOM_KEY_MAP("PageUp", PAGE_UP), + // Key Enum Value + DOM_KEY_MAP("ArrowDown", ARROW_DOWN, 0x0301), + DOM_KEY_MAP("ArrowLeft", ARROW_LEFT, 0x0302), + DOM_KEY_MAP("ArrowRight", ARROW_RIGHT, 0x0303), + DOM_KEY_MAP("ArrowUp", ARROW_UP, 0x0304), + DOM_KEY_MAP("End", END, 0x0305), + DOM_KEY_MAP("Home", HOME, 0x0306), + DOM_KEY_MAP("PageDown", PAGE_DOWN, 0x0307), + DOM_KEY_MAP("PageUp", PAGE_UP, 0x0308), // ========================================================= // Editing Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-editing + // http://www.w3.org/TR/uievents-key/#keys-editing // ========================================================= - // Key Enum + // Key Enum Value // "Backspace" is encoded in the Unicode space. - DOM_KEY_MAP("Clear", CLEAR), - DOM_KEY_MAP("Copy", COPY), - DOM_KEY_MAP("CrSel", CR_SEL), // Cursor Select - DOM_KEY_MAP("Cut", CUT), + DOM_KEY_MAP("Clear", CLEAR, 0x0401), + DOM_KEY_MAP("Copy", COPY, 0x0402), + // "CrSel" is short for "Cursor Select": + DOM_KEY_MAP("CrSel", CR_SEL, 0x0403), + DOM_KEY_MAP("Cut", CUT, 0x0404), // "Delete" is encoded in the Unicode space. - DOM_KEY_MAP("EraseEof", ERASE_EOF), // Erase to End of Field - DOM_KEY_MAP("ExSel", EX_SEL), // Extend Selection - DOM_KEY_MAP("Insert", INSERT), - DOM_KEY_MAP("Paste", PASTE), - DOM_KEY_MAP("Redo", REDO), - DOM_KEY_MAP("Undo", UNDO), + // "EraseEof" is short for "Erase to End of Field": + DOM_KEY_MAP("EraseEof", ERASE_EOF, 0x0405), + // "ExSel" is short for "Extend Selection": + DOM_KEY_MAP("ExSel", EX_SEL, 0x0406), + DOM_KEY_MAP("Insert", INSERT, 0x0407), + DOM_KEY_MAP("Paste", PASTE, 0x0408), + DOM_KEY_MAP("Redo", REDO, 0x0409), + DOM_KEY_MAP("Undo", UNDO, 0x040A), // ==================================================== // UI Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-ui + // http://www.w3.org/TR/uievents-key/#keys-ui // ==================================================== - // Key Enum - DOM_KEY_MAP("Accept", ACCEPT), // Accept current IME conversion - DOM_KEY_MAP("Again", AGAIN), // Repeat an action - DOM_KEY_MAP("Attn", ATTN), // Attention - DOM_KEY_MAP("Cancel", CANCEL), - DOM_KEY_MAP("ContextMenu", CONTEXT_MENU), + // Key Enum Value + // "Accept" accepts the current IME conversion: + DOM_KEY_MAP("Accept", ACCEPT, 0x0501), + // "Again" is used to repeat an action: + DOM_KEY_MAP("Again", AGAIN, 0x0502), + // "Attn" is short for "Attention": + DOM_KEY_MAP("Attn", ATTN, 0x0503), + DOM_KEY_MAP("Cancel", CANCEL, 0x0504), + DOM_KEY_MAP("ContextMenu", CONTEXT_MENU, 0x0505), // "Escape" is encoded in the Unicode space. - DOM_KEY_MAP("Execute", EXECUTE), - DOM_KEY_MAP("Find", FIND), - DOM_KEY_MAP("Help", HELP), - DOM_KEY_MAP("Pause", PAUSE), // Program state - not for media - DOM_KEY_MAP("Play", PLAY), // Program state - not for media - DOM_KEY_MAP("Props", PROPS), // Properties - DOM_KEY_MAP("Select", SELECT), - DOM_KEY_MAP("ZoomIn", ZOOM_IN), - DOM_KEY_MAP("ZoomOut", ZOOM_OUT), + DOM_KEY_MAP("Execute", EXECUTE, 0x0506), + DOM_KEY_MAP("Find", FIND, 0x0507), + DOM_KEY_MAP("Help", HELP, 0x0508), + // "Pause" applies to program state - not to playing media: + DOM_KEY_MAP("Pause", PAUSE, 0x0509), + // "Play" applies to program state - not to playing media: + DOM_KEY_MAP("Play", PLAY, 0x050A), + // "Props" is short for "Properties": + DOM_KEY_MAP("Props", PROPS, 0x050B), + DOM_KEY_MAP("Select", SELECT, 0x050C), + DOM_KEY_MAP("ZoomIn", ZOOM_IN, 0x050D), + DOM_KEY_MAP("ZoomOut", ZOOM_OUT, 0x050E), // ======================================================== // Device Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-device + // http://www.w3.org/TR/uievents-key/#keys-device // ======================================================== - // Key Enum - DOM_KEY_MAP("BrightnessDown", BRIGHTNESS_DOWN), - DOM_KEY_MAP("BrightnessUp", BRIGHTNESS_UP), - DOM_KEY_MAP("Camera", CAMERA), - DOM_KEY_MAP("Eject", EJECT), - DOM_KEY_MAP("LogOff", LOG_OFF), - DOM_KEY_MAP("Power", POWER), - DOM_KEY_MAP("PowerOff", POWER_OFF), - DOM_KEY_MAP("PrintScreen", PRINT_SCREEN), - DOM_KEY_MAP("Hibernate", HIBERNATE), - DOM_KEY_MAP("Standby", STANDBY), - DOM_KEY_MAP("WakeUp", WAKE_UP), + // Key Enum Value + DOM_KEY_MAP("BrightnessDown", BRIGHTNESS_DOWN, 0x0601), + DOM_KEY_MAP("BrightnessUp", BRIGHTNESS_UP, 0x0602), + // "Camera" moved to Mobile Phone Keys section. + // ("Camera", CAMERA, 0x0603), + DOM_KEY_MAP("Eject", EJECT, 0x0604), + DOM_KEY_MAP("LogOff", LOG_OFF, 0x0605), + DOM_KEY_MAP("Power", POWER, 0x0606), + DOM_KEY_MAP("PowerOff", POWER_OFF, 0x0607), + DOM_KEY_MAP("PrintScreen", PRINT_SCREEN, 0x0608), + DOM_KEY_MAP("Hibernate", HIBERNATE, 0x0609), + DOM_KEY_MAP("Standby", STANDBY, 0x060A), + DOM_KEY_MAP("WakeUp", WAKE_UP, 0x060B), // ============================================================= // IME and Composition Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-composition + // http://www.w3.org/TR/uievents-key/#keys-composition // ============================================================= - // Key Enum - DOM_KEY_MAP("AllCandidates", ALL_CANDIDATES), - DOM_KEY_MAP("Alphanumeric", ALPHANUMERIC), - DOM_KEY_MAP("CodeInput", CODE_INPUT), - DOM_KEY_MAP("Compose", COMPOSE), - DOM_KEY_MAP("Convert", CONVERT), + // Key Enum Value + DOM_KEY_MAP("AllCandidates", ALL_CANDIDATES, 0x0701), + DOM_KEY_MAP("Alphanumeric", ALPHANUMERIC, 0x0702), + DOM_KEY_MAP("CodeInput", CODE_INPUT, 0x0703), + DOM_KEY_MAP("Compose", COMPOSE, 0x0704), + DOM_KEY_MAP("Convert", CONVERT, 0x0705), // "Dead" is represented as a flag plus combining character. - DOM_KEY_MAP("FinalMode", FINAL_MODE), - DOM_KEY_MAP("GroupFirst", GROUP_FIRST), - DOM_KEY_MAP("GroupLast", GROUP_LAST), - DOM_KEY_MAP("GroupNext", GROUP_NEXT), - DOM_KEY_MAP("GroupPrevious", GROUP_PREVIOUS), - DOM_KEY_MAP("ModeChange", MODE_CHANGE), - DOM_KEY_MAP("NextCandidate", NEXT_CANDIDATE), - DOM_KEY_MAP("NonConvert", NON_CONVERT), - DOM_KEY_MAP("PreviousCandidate", PREVIOUS_CANDIDATE), - DOM_KEY_MAP("Process", PROCESS), - DOM_KEY_MAP("SingleCandidate", SINGLE_CANDIDATE), + DOM_KEY_MAP("FinalMode", FINAL_MODE, 0x0706), + DOM_KEY_MAP("GroupFirst", GROUP_FIRST, 0x0707), + DOM_KEY_MAP("GroupLast", GROUP_LAST, 0x0708), + DOM_KEY_MAP("GroupNext", GROUP_NEXT, 0x0709), + DOM_KEY_MAP("GroupPrevious", GROUP_PREVIOUS, 0x070A), + DOM_KEY_MAP("ModeChange", MODE_CHANGE, 0x070B), + DOM_KEY_MAP("NextCandidate", NEXT_CANDIDATE, 0x070C), + DOM_KEY_MAP("NonConvert", NON_CONVERT, 0x070D), + DOM_KEY_MAP("PreviousCandidate", PREVIOUS_CANDIDATE, 0x070E), + DOM_KEY_MAP("Process", PROCESS, 0x070F), + DOM_KEY_MAP("SingleCandidate", SINGLE_CANDIDATE, 0x0710), // Keys specific to Korean keyboards - DOM_KEY_MAP("HangulMode", HANGUL_MODE), - DOM_KEY_MAP("HanjaMode", HANJA_MODE), - DOM_KEY_MAP("JunjaMode", JUNJA_MODE), + DOM_KEY_MAP("HangulMode", HANGUL_MODE, 0x0711), + DOM_KEY_MAP("HanjaMode", HANJA_MODE, 0x0712), + DOM_KEY_MAP("JunjaMode", JUNJA_MODE, 0x0713), // Keys specific to Japanese keyboards - DOM_KEY_MAP("Eisu", EISU), - DOM_KEY_MAP("Hankaku", HANKAKU), - DOM_KEY_MAP("Hiragana", HIRAGANA), - DOM_KEY_MAP("HiraganaKatakana", HIRAGANA_KATAKANA), - DOM_KEY_MAP("KanaMode", KANA_MODE), - DOM_KEY_MAP("KanjiMode", KANJI_MODE), - DOM_KEY_MAP("Katakana", KATAKANA), - DOM_KEY_MAP("Romaji", ROMAJI), - DOM_KEY_MAP("Zenkaku", ZENKAKU), - DOM_KEY_MAP("ZenkakuHankaku", ZENKAKU_HANKAKU), + DOM_KEY_MAP("Eisu", EISU, 0x0714), + DOM_KEY_MAP("Hankaku", HANKAKU, 0x0715), + DOM_KEY_MAP("Hiragana", HIRAGANA, 0x0716), + DOM_KEY_MAP("HiraganaKatakana", HIRAGANA_KATAKANA, 0x0717), + DOM_KEY_MAP("KanaMode", KANA_MODE, 0x0718), + DOM_KEY_MAP("KanjiMode", KANJI_MODE, 0x0719), + DOM_KEY_MAP("Katakana", KATAKANA, 0x071A), + DOM_KEY_MAP("Romaji", ROMAJI, 0x071B), + DOM_KEY_MAP("Zenkaku", ZENKAKU, 0x071C), + DOM_KEY_MAP("ZenkakuHankaku", ZENKAKU_HANKAKU, 0x071D), // ========================================================== // General-Purpose Function Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-function + // http://www.w3.org/TR/uievents-key/#keys-function // ========================================================== // DOM defines open-ended sets, but if we want a finite numeric encoding we // need to draw the line somewhere. USB and Windows (VK) and Linux (evdev) // all support 24, so we define 24. - // Key Enum - DOM_KEY_MAP("F1", F1), - DOM_KEY_MAP("F2", F2), - DOM_KEY_MAP("F3", F3), - DOM_KEY_MAP("F4", F4), - DOM_KEY_MAP("F5", F5), - DOM_KEY_MAP("F6", F6), - DOM_KEY_MAP("F7", F7), - DOM_KEY_MAP("F8", F8), - DOM_KEY_MAP("F9", F9), - DOM_KEY_MAP("F10", F10), - DOM_KEY_MAP("F11", F11), - DOM_KEY_MAP("F12", F12), - DOM_KEY_MAP("F13", F13), - DOM_KEY_MAP("F14", F14), - DOM_KEY_MAP("F15", F15), - DOM_KEY_MAP("F16", F16), - DOM_KEY_MAP("F17", F17), - DOM_KEY_MAP("F18", F18), - DOM_KEY_MAP("F19", F19), - DOM_KEY_MAP("F20", F20), - DOM_KEY_MAP("F21", F21), - DOM_KEY_MAP("F22", F22), - DOM_KEY_MAP("F23", F23), - DOM_KEY_MAP("F24", F24), - DOM_KEY_MAP("Soft1", SOFT1), - DOM_KEY_MAP("Soft2", SOFT2), - DOM_KEY_MAP("Soft3", SOFT3), - DOM_KEY_MAP("Soft4", SOFT4), - DOM_KEY_MAP("Soft5", SOFT5), - DOM_KEY_MAP("Soft6", SOFT6), - DOM_KEY_MAP("Soft7", SOFT7), - DOM_KEY_MAP("Soft8", SOFT8), + // Key Enum Value + DOM_KEY_MAP("F1", F1, 0x0801), + DOM_KEY_MAP("F2", F2, 0x0802), + DOM_KEY_MAP("F3", F3, 0x0803), + DOM_KEY_MAP("F4", F4, 0x0804), + DOM_KEY_MAP("F5", F5, 0x0805), + DOM_KEY_MAP("F6", F6, 0x0806), + DOM_KEY_MAP("F7", F7, 0x0807), + DOM_KEY_MAP("F8", F8, 0x0808), + DOM_KEY_MAP("F9", F9, 0x0809), + DOM_KEY_MAP("F10", F10, 0x080A), + DOM_KEY_MAP("F11", F11, 0x080B), + DOM_KEY_MAP("F12", F12, 0x080C), + DOM_KEY_MAP("F13", F13, 0x080D), + DOM_KEY_MAP("F14", F14, 0x080E), + DOM_KEY_MAP("F15", F15, 0x080F), + DOM_KEY_MAP("F16", F16, 0x0810), + DOM_KEY_MAP("F17", F17, 0x0811), + DOM_KEY_MAP("F18", F18, 0x0812), + DOM_KEY_MAP("F19", F19, 0x0813), + DOM_KEY_MAP("F20", F20, 0x0814), + DOM_KEY_MAP("F21", F21, 0x0815), + DOM_KEY_MAP("F22", F22, 0x0816), + DOM_KEY_MAP("F23", F23, 0x0817), + DOM_KEY_MAP("F24", F24, 0x0818), + DOM_KEY_MAP("Soft1", SOFT1, 0x0901), + DOM_KEY_MAP("Soft2", SOFT2, 0x0902), + DOM_KEY_MAP("Soft3", SOFT3, 0x0903), + DOM_KEY_MAP("Soft4", SOFT4, 0x0904), + DOM_KEY_MAP("Soft5", SOFT5, 0x0905), + DOM_KEY_MAP("Soft6", SOFT6, 0x0906), + DOM_KEY_MAP("Soft7", SOFT7, 0x0907), + DOM_KEY_MAP("Soft8", SOFT8, 0x0908), // ============================================================ // Multimedia Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-multimedia + // http://www.w3.org/TR/uievents-key/#keys-multimedia + // ============================================================ + + // Note: some keys were previously listed in the Media Controller Keys + // section of the spec, and retain their original numbering (0x0D__). + + // Key Enum Value + DOM_KEY_MAP("ChannelDown", CHANNEL_DOWN, 0x0D0A), + DOM_KEY_MAP("ChannelUp", CHANNEL_UP, 0x0D0B), + DOM_KEY_MAP("Close", CLOSE, 0x0A01), + DOM_KEY_MAP("MailForward", MAIL_FORWARD, 0x0A02), + DOM_KEY_MAP("MailReply", MAIL_REPLY, 0x0A03), + DOM_KEY_MAP("MailSend", MAIL_SEND, 0x0A04), + DOM_KEY_MAP("MediaFastForward", MEDIA_FAST_FORWARD, 0x0D2C), + DOM_KEY_MAP("MediaPause", MEDIA_PAUSE, 0x0D2E), + DOM_KEY_MAP("MediaPlay", MEDIA_PLAY, 0x0D2F), + DOM_KEY_MAP("MediaPlayPause", MEDIA_PLAY_PAUSE, 0x0A05), + DOM_KEY_MAP("MediaRecord", MEDIA_RECORD, 0x0D30), + DOM_KEY_MAP("MediaRewind", MEDIA_REWIND, 0x0D31), + // "MediaSelect" was removed from the spec. + // ("MediaSelect", MEDIA_SELECT, 0x0A06), + DOM_KEY_MAP("MediaStop", MEDIA_STOP, 0x0A07), + DOM_KEY_MAP("MediaTrackNext", MEDIA_TRACK_NEXT, 0x0A08), + DOM_KEY_MAP("MediaTrackPrevious", MEDIA_TRACK_PREVIOUS, 0x0A09), + DOM_KEY_MAP("New", NEW, 0x0A0A), + DOM_KEY_MAP("Open", OPEN, 0x0A0B), + DOM_KEY_MAP("Print", PRINT, 0x0A0C), + DOM_KEY_MAP("Save", SAVE, 0x0A0D), + DOM_KEY_MAP("SpellCheck", SPELL_CHECK, 0x0A0E), + // ============================================================ + // Multimedia Numpad Keys + // http://www.w3.org/TR/uievents-key/#keys-multimedia-numpad + // ============================================================ + + DOM_KEY_MAP("Key11", KEY11, 0x1201), + DOM_KEY_MAP("Key12", KEY12, 0x1202), + + // ======================================================= + // Audio Keys + // http://www.w3.org/TR/uievents-key/#keys-audio + // ======================================================= + + // Note: some keys were previously listed in the Multimedia Keys + // (0x0A__) or Media Controller Keys (0x0D__) sections of the spec, + // and retain their original numbering. + + // Key Enum Value + DOM_KEY_MAP("AudioBalanceLeft", AUDIO_BALANCE_LEFT, 0x0D01), + DOM_KEY_MAP("AudioBalanceRight", AUDIO_BALANCE_RIGHT, 0x0D02), + DOM_KEY_MAP("AudioBassDown", AUDIO_BASS_DOWN, 0x0E01), + DOM_KEY_MAP("AudioBassBoostDown", AUDIO_BASS_BOOST_DOWN, 0x0D03), + DOM_KEY_MAP("AudioBassBoostToggle", AUDIO_BASS_BOOST_TOGGLE, 0x0E02), + DOM_KEY_MAP("AudioBassBoostUp", AUDIO_BASS_BOOST_UP, 0x0D04), + DOM_KEY_MAP("AudioBassUp", AUDIO_BASS_UP, 0x0E03), + DOM_KEY_MAP("AudioFaderFront", AUDIO_FADER_FRONT, 0x0D05), + DOM_KEY_MAP("AudioFaderRear", AUDIO_FADER_REAR, 0x0D06), + DOM_KEY_MAP("AudioSurroundModeNext",AUDIO_SURROUND_MODE_NEXT, 0x0D07), + DOM_KEY_MAP("AudioTrebleDown", AUDIO_TREBLE_DOWN, 0x0E04), + DOM_KEY_MAP("AudioTrebleUp", AUDIO_TREBLE_UP, 0x0E05), + DOM_KEY_MAP("AudioVolumeDown", AUDIO_VOLUME_DOWN, 0x0A0F), + DOM_KEY_MAP("AudioVolumeUp", AUDIO_VOLUME_UP, 0x0A10), + DOM_KEY_MAP("AudioVolumeMute", AUDIO_VOLUME_MUTE, 0x0A11), + DOM_KEY_MAP("MicrophoneToggle", MICROPHONE_TOGGLE, 0x0E06), + DOM_KEY_MAP("MicrophoneVolumeDown", MICROPHONE_VOLUME_DOWN, 0x0E07), + DOM_KEY_MAP("MicrophoneVolumeUp", MICROPHONE_VOLUME_UP, 0x0E08), + DOM_KEY_MAP("MicrophoneVolumeMute", MICROPHONE_VOLUME_MUTE, 0x0E09), + + // ======================================================== + // Speech Keys + // http://www.w3.org/TR/uievents-key/#keys-speech + // ======================================================== - // Key Enum - DOM_KEY_MAP("Close", CLOSE), - DOM_KEY_MAP("MailForward", MAIL_FORWARD), - DOM_KEY_MAP("MailReply", MAIL_REPLY), - DOM_KEY_MAP("MailSend", MAIL_SEND), - DOM_KEY_MAP("MediaPlayPause", MEDIA_PLAY_PAUSE), - DOM_KEY_MAP("MediaSelect", MEDIA_SELECT), - DOM_KEY_MAP("MediaStop", MEDIA_STOP), - DOM_KEY_MAP("MediaTrackNext", MEDIA_TRACK_NEXT), - DOM_KEY_MAP("MediaTrackPrevious", MEDIA_TRACK_PREVIOUS), - DOM_KEY_MAP("New", NEW), - DOM_KEY_MAP("Open", OPEN), - DOM_KEY_MAP("Print", PRINT), - DOM_KEY_MAP("Save", SAVE), - DOM_KEY_MAP("SpellCheck", SPELL_CHECK), - DOM_KEY_MAP("VolumeDown", VOLUME_DOWN), - DOM_KEY_MAP("VolumeUp", VOLUME_UP), - DOM_KEY_MAP("VolumeMute", VOLUME_MUTE), + // Key Enum Value + DOM_KEY_MAP("SpeechCorrectionList", SPEECH_CORRECTION_LIST, 0x0F01), + DOM_KEY_MAP("SpeechInputToggle", SPEECH_INPUT_TOGGLE, 0x0F02), // ====================================================== // Application Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-apps + // http://www.w3.org/TR/uievents-key/#keys-apps // ====================================================== - // Key Enum - DOM_KEY_MAP("LaunchCalculator", LAUNCH_CALCULATOR), // Application 2 - DOM_KEY_MAP("LaunchCalendar", LAUNCH_CALENDAR), - DOM_KEY_MAP("LaunchMail", LAUNCH_MAIL), - DOM_KEY_MAP("LaunchMediaPlayer", LAUNCH_MEDIA_PLAYER), - DOM_KEY_MAP("LaunchMusicPlayer", LAUNCH_MUSIC_PLAYER), - DOM_KEY_MAP("LaunchMyComputer", LAUNCH_MY_COMPUTER), // Application 1 - DOM_KEY_MAP("LaunchScreenSaver", LAUNCH_SCREEN_SAVER), - DOM_KEY_MAP("LaunchSpreadsheet", LAUNCH_SPREADSHEET), - DOM_KEY_MAP("LaunchWebBrowser", LAUNCH_WEB_BROWSER), - DOM_KEY_MAP("LaunchWebCam", LAUNCH_WEB_CAM), - DOM_KEY_MAP("LaunchWordProcessor", LAUNCH_WORD_PROCESSOR), + // Key Enum Value + // "LaunchCalculator" is equivalent to "Launch Application 2": + DOM_KEY_MAP("LaunchCalculator", LAUNCH_CALCULATOR, 0x0B01), + DOM_KEY_MAP("LaunchCalendar", LAUNCH_CALENDAR, 0x0B02), + DOM_KEY_MAP("LaunchContacts", LAUNCH_CONTACTS, 0x0B0C), + DOM_KEY_MAP("LaunchMail", LAUNCH_MAIL, 0x0B03), + DOM_KEY_MAP("LaunchMediaPlayer", LAUNCH_MEDIA_PLAYER, 0x0B04), + DOM_KEY_MAP("LaunchMusicPlayer", LAUNCH_MUSIC_PLAYER, 0x0B05), + // "LaunchMyComputer" is equivalent to "Launch Application 1": + DOM_KEY_MAP("LaunchMyComputer", LAUNCH_MY_COMPUTER, 0x0B06), + DOM_KEY_MAP("LaunchPhone", LAUNCH_PHONE, 0x0B0D), + DOM_KEY_MAP("LaunchScreenSaver", LAUNCH_SCREEN_SAVER, 0x0B07), + DOM_KEY_MAP("LaunchSpreadsheet", LAUNCH_SPREADSHEET, 0x0B08), + DOM_KEY_MAP("LaunchWebBrowser", LAUNCH_WEB_BROWSER, 0x0B09), + DOM_KEY_MAP("LaunchWebCam", LAUNCH_WEB_CAM, 0x0B0A), + DOM_KEY_MAP("LaunchWordProcessor", LAUNCH_WORD_PROCESSOR, 0x0B0B), // ========================================================= // Browser Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-browser + // http://www.w3.org/TR/uievents-key/#keys-browser + // ========================================================= + + // Key Enum Value + DOM_KEY_MAP("BrowserBack", BROWSER_BACK, 0x0C01), + DOM_KEY_MAP("BrowserFavorites", BROWSER_FAVORITES, 0x0C02), + DOM_KEY_MAP("BrowserForward", BROWSER_FORWARD, 0x0C03), + DOM_KEY_MAP("BrowserHome", BROWSER_HOME, 0x0C04), + DOM_KEY_MAP("BrowserRefresh", BROWSER_REFRESH, 0x0C05), + DOM_KEY_MAP("BrowserSearch", BROWSER_SEARCH, 0x0C06), + DOM_KEY_MAP("BrowserStop", BROWSER_STOP, 0x0C07), + + // ========================================================= + // Mobile Phone Keys + // http://www.w3.org/TR/uievents-key/#keys-mobile + // ========================================================= + + // Note: some keys were previously listed in the Device Keys (0x06__) + // section and retain their original numbering. + + // Key Enum Value + DOM_KEY_MAP("AppSwitch", APP_SWITCH, 0x1001), + DOM_KEY_MAP("Call", CALL, 0x1002), + DOM_KEY_MAP("Camera", CAMERA, 0x0603), + DOM_KEY_MAP("CameraFocus", CAMERA_FOCUS, 0x1003), + DOM_KEY_MAP("EndCall", END_CALL, 0x1004), + DOM_KEY_MAP("GoBack", GO_BACK, 0x1005), + DOM_KEY_MAP("GoHome", GO_HOME, 0x1006), + DOM_KEY_MAP("HeadsetHook", HEADSET_HOOK, 0x1007), + DOM_KEY_MAP("LastNumberRedial", LAST_NUMBER_REDIAL, 0x1008), + DOM_KEY_MAP("Notification", NOTIFICATION, 0x1009), + DOM_KEY_MAP("MannerMode", MANNER_MODE, 0x100A), + DOM_KEY_MAP("VoiceDial", VOICE_DIAL, 0x100B), + + // ========================================================= + // TV Keys + // http://www.w3.org/TR/uievents-key/#keys-tv // ========================================================= - // Key Enum - DOM_KEY_MAP("BrowserBack", BROWSER_BACK), - DOM_KEY_MAP("BrowserFavorites", BROWSER_FAVORITES), - DOM_KEY_MAP("BrowserForward", BROWSER_FORWARD), - DOM_KEY_MAP("BrowserHome", BROWSER_HOME), - DOM_KEY_MAP("BrowserRefresh", BROWSER_REFRESH), - DOM_KEY_MAP("BrowserSearch", BROWSER_SEARCH), - DOM_KEY_MAP("BrowserStop", BROWSER_STOP), + // Note: some keys were previously listed in the Media Controller + // Keys (0x0D__) section and retain their original numbering. + + // Key Enum Value + DOM_KEY_MAP("TV", TV, 0x0D49), + DOM_KEY_MAP("TV3DMode", TV_3D_MODE, 0x1101), + DOM_KEY_MAP("TVAntennaCable", TV_ANTENNA_CABLE, 0x1102), + DOM_KEY_MAP("TVAudioDescription", TV_AUDIO_DESCRIPTION, 0x1103), + DOM_KEY_MAP("TVAudioDescriptionMixDown", TV_AUDIO_DESCRIPTION_MIX_DOWN, + 0x1104), + DOM_KEY_MAP("TVAudioDescriptionMixUp", TV_AUDIO_DESCRIPTION_MIX_UP, + 0x1105), + DOM_KEY_MAP("TVContentsMenu", TV_CONTENTS_MENU, 0x1106), + DOM_KEY_MAP("TVDataService", TV_DATA_SERVICE, 0x1107), + DOM_KEY_MAP("TVInput", TV_INPUT, 0x0D4A), + DOM_KEY_MAP("TVInputComponent1", TV_INPUT_COMPONENT1, 0x1108), + DOM_KEY_MAP("TVInputComponent2", TV_INPUT_COMPONENT2, 0x1109), + DOM_KEY_MAP("TVInputComposite1", TV_INPUT_COMPOSITE1, 0x110A), + DOM_KEY_MAP("TVInputComposite2", TV_INPUT_COMPOSITE2, 0x110B), + DOM_KEY_MAP("TVInputHDMI1", TV_INPUT_HDMI1, 0x110C), + DOM_KEY_MAP("TVInputHDMI2", TV_INPUT_HDMI2, 0x110D), + DOM_KEY_MAP("TVInputHDMI3", TV_INPUT_HDMI3, 0x110E), + DOM_KEY_MAP("TVInputHDMI4", TV_INPUT_HDMI4, 0x110F), + DOM_KEY_MAP("TVInputVGA1", TV_INPUT_VGA1, 0x1110), + DOM_KEY_MAP("TVMediaContext", TV_MEDIA_CONTEXT, 0x1111), + DOM_KEY_MAP("TVNetwork", TV_NETWORK, 0x1112), + DOM_KEY_MAP("TVNumberEntry", TV_NUMBER_ENTRY, 0x1113), + DOM_KEY_MAP("TVPower", TV_POWER, 0x0D4B), + DOM_KEY_MAP("TVRadioService", TV_RADIO_SERVICE, 0x1114), + DOM_KEY_MAP("TVSatellite", TV_SATELLITE, 0x1115), + DOM_KEY_MAP("TVSatelliteBC", TV_SATELLITE_BC, 0x1116), + DOM_KEY_MAP("TVSatelliteCS", TV_SATELLITE_CS, 0x1117), + DOM_KEY_MAP("TVSatelliteToggle", TV_SATELLITE_TOGGLE, 0x1118), + DOM_KEY_MAP("TVTerrestrialAnalog", TV_TERRESTRIAL_ANALOG, 0x1119), + DOM_KEY_MAP("TVTerrestrialDigital", TV_TERRESTRIAL_DIGITAL, 0x111A), + DOM_KEY_MAP("TVTimer", TV_TIMER, 0x111B), // ================================================================== // Media Controller Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-media-controller + // http://www.w3.org/TR/uievents-key/#keys-media-controller // ================================================================== - // Key Enum - DOM_KEY_MAP("AudioBalanceLeft", AUDIO_BALANCE_LEFT), - DOM_KEY_MAP("AudioBalanceRight", AUDIO_BALANCE_RIGHT), - DOM_KEY_MAP("AudioBassBoostDown", AUDIO_BASS_BOOST_DOWN), - DOM_KEY_MAP("AudioBassBoostUp", AUDIO_BASS_BOOST_UP), - DOM_KEY_MAP("AudioFaderFront", AUDIO_FADER_FRONT), - DOM_KEY_MAP("AudioFaderRear", AUDIO_FADER_REAR), - DOM_KEY_MAP("AudioSurroundModeNext",AUDIO_SURROUND_MODE_NEXT), - DOM_KEY_MAP("AVRInput", AVR_INPUT), - DOM_KEY_MAP("AVRPower", AVR_POWER), - DOM_KEY_MAP("ChannelDown", CHANNEL_DOWN), - DOM_KEY_MAP("ChannelUp", CHANNEL_UP), - DOM_KEY_MAP("ColorF0Red", COLOR_F0_RED), - DOM_KEY_MAP("ColorF1Green", COLOR_F1_GREEN), - DOM_KEY_MAP("ColorF2Yellow", COLOR_F2_YELLOW), - DOM_KEY_MAP("ColorF3Blue", COLOR_F3_BLUE), - DOM_KEY_MAP("ColorF4Grey", COLOR_F4_GREY), - DOM_KEY_MAP("ColorF5Brown", COLOR_F5_BROWN), - DOM_KEY_MAP("ClosedCaptionToggle", CLOSED_CAPTION_TOGGLE), - DOM_KEY_MAP("Dimmer", DIMMER), - DOM_KEY_MAP("DisplaySwap", DISPLAY_SWAP), - DOM_KEY_MAP("Exit", EXIT), - DOM_KEY_MAP("FavoriteClear0", FAVORITE_CLEAR0), - DOM_KEY_MAP("FavoriteClear1", FAVORITE_CLEAR1), - DOM_KEY_MAP("FavoriteClear2", FAVORITE_CLEAR2), - DOM_KEY_MAP("FavoriteClear3", FAVORITE_CLEAR3), - DOM_KEY_MAP("FavoriteRecall0", FAVORITE_RECALL0), - DOM_KEY_MAP("FavoriteRecall1", FAVORITE_RECALL1), - DOM_KEY_MAP("FavoriteRecall2", FAVORITE_RECALL2), - DOM_KEY_MAP("FavoriteRecall3", FAVORITE_RECALL3), - DOM_KEY_MAP("FavoriteStore0", FAVORITE_STORE0), - DOM_KEY_MAP("FavoriteStore1", FAVORITE_STORE1), - DOM_KEY_MAP("FavoriteStore2", FAVORITE_STORE2), - DOM_KEY_MAP("FavoriteStore3", FAVORITE_STORE3), - DOM_KEY_MAP("Guide", GUIDE), - DOM_KEY_MAP("GuideNextDay", GUIDE_NEXT_DAY), - DOM_KEY_MAP("GuidePreviousDay", GUIDE_PREVIOUS_DAY), - DOM_KEY_MAP("Info", INFO), - DOM_KEY_MAP("InstantReplay", INSTANT_REPLAY), - DOM_KEY_MAP("Link", LINK), - DOM_KEY_MAP("ListProgram", LIST_PROGRAM), - DOM_KEY_MAP("LiveContent", LIVE_CONTENT), - DOM_KEY_MAP("Lock", LOCK), - DOM_KEY_MAP("MediaApps", MEDIA_APPS), - DOM_KEY_MAP("MediaFastForward", MEDIA_FAST_FORWARD), - DOM_KEY_MAP("MediaLast", MEDIA_LAST), - DOM_KEY_MAP("MediaPause", MEDIA_PAUSE), - DOM_KEY_MAP("MediaPlay", MEDIA_PLAY), - DOM_KEY_MAP("MediaRecord", MEDIA_RECORD), - DOM_KEY_MAP("MediaRewind", MEDIA_REWIND), - DOM_KEY_MAP("MediaSkip", MEDIA_SKIP), - DOM_KEY_MAP("NextFavoriteChannel", NEXT_FAVORITE_CHANNEL), - DOM_KEY_MAP("NextUserProfile", NEXT_USER_PROFILE), - DOM_KEY_MAP("OnDemand", ON_DEMAND), - DOM_KEY_MAP("PinPDown", PINP_DOWN), - DOM_KEY_MAP("PinPMove", PINP_MOVE), - DOM_KEY_MAP("PinPToggle", PINP_TOGGLE), - DOM_KEY_MAP("PinPUp", PINP_UP), - DOM_KEY_MAP("PlaySpeedDown", PLAY_SPEED_DOWN), - DOM_KEY_MAP("PlaySpeedReset", PLAY_SPEED_RESET), - DOM_KEY_MAP("PlaySpeedUp", PLAY_SPEED_UP), - DOM_KEY_MAP("RandomToggle", RANDOM_TOGGLE), - DOM_KEY_MAP("RcLowBattery", RC_LOW_BATTERY), - DOM_KEY_MAP("RecordSpeedNext", RECORD_SPEED_NEXT), - DOM_KEY_MAP("RfBypass", RF_BYPASS), - DOM_KEY_MAP("ScanChannelsToggle", SCAN_CHANNELS_TOGGLE), - DOM_KEY_MAP("ScreenModeNext", SCREEN_MODE_NEXT), - DOM_KEY_MAP("Settings", SETTINGS), - DOM_KEY_MAP("SplitScreenToggle", SPLIT_SCREEN_TOGGLE), - DOM_KEY_MAP("STBInput", STB_INPUT), - DOM_KEY_MAP("STBPower", STB_POWER), - DOM_KEY_MAP("Subtitle", SUBTITLE), - DOM_KEY_MAP("Teletext", TELETEXT), - DOM_KEY_MAP("TV", T_V), - DOM_KEY_MAP("TVInput", TV_INPUT), - DOM_KEY_MAP("TVPower", TV_POWER), - DOM_KEY_MAP("VideoModeNext", VIDEO_MODE_NEXT), - DOM_KEY_MAP("Wink", WINK), - DOM_KEY_MAP("ZoomToggle", ZOOM_TOGGLE), - - DOM_KEY_MAP_END + // Key Enum Value + DOM_KEY_MAP("AVRInput", AVR_INPUT, 0x0D08), + DOM_KEY_MAP("AVRPower", AVR_POWER, 0x0D09), + // Moved to Multimedia Keys section: + // ("ChannelDown", CHANNEL_DOWN, 0x0D0A), + // ("ChannelUp", CHANNEL_UP, 0x0D0B), + DOM_KEY_MAP("ColorF0Red", COLOR_F0_RED, 0x0D0C), + DOM_KEY_MAP("ColorF1Green", COLOR_F1_GREEN, 0x0D0D), + DOM_KEY_MAP("ColorF2Yellow", COLOR_F2_YELLOW, 0x0D0E), + DOM_KEY_MAP("ColorF3Blue", COLOR_F3_BLUE, 0x0D0F), + DOM_KEY_MAP("ColorF4Grey", COLOR_F4_GREY, 0x0D10), + DOM_KEY_MAP("ColorF5Brown", COLOR_F5_BROWN, 0x0D11), + DOM_KEY_MAP("ClosedCaptionToggle", CLOSED_CAPTION_TOGGLE, 0x0D12), + DOM_KEY_MAP("Dimmer", DIMMER, 0x0D13), + DOM_KEY_MAP("DisplaySwap", DISPLAY_SWAP, 0x0D14), + DOM_KEY_MAP("DVR", DVR, 0x0D4F), + DOM_KEY_MAP("Exit", EXIT, 0x0D15), + DOM_KEY_MAP("FavoriteClear0", FAVORITE_CLEAR0, 0x0D16), + DOM_KEY_MAP("FavoriteClear1", FAVORITE_CLEAR1, 0x0D17), + DOM_KEY_MAP("FavoriteClear2", FAVORITE_CLEAR2, 0x0D18), + DOM_KEY_MAP("FavoriteClear3", FAVORITE_CLEAR3, 0x0D19), + DOM_KEY_MAP("FavoriteRecall0", FAVORITE_RECALL0, 0x0D1A), + DOM_KEY_MAP("FavoriteRecall1", FAVORITE_RECALL1, 0x0D1B), + DOM_KEY_MAP("FavoriteRecall2", FAVORITE_RECALL2, 0x0D1C), + DOM_KEY_MAP("FavoriteRecall3", FAVORITE_RECALL3, 0x0D1D), + DOM_KEY_MAP("FavoriteStore0", FAVORITE_STORE0, 0x0D1E), + DOM_KEY_MAP("FavoriteStore1", FAVORITE_STORE1, 0x0D1F), + DOM_KEY_MAP("FavoriteStore2", FAVORITE_STORE2, 0x0D20), + DOM_KEY_MAP("FavoriteStore3", FAVORITE_STORE3, 0x0D21), + DOM_KEY_MAP("Guide", GUIDE, 0x0D22), + DOM_KEY_MAP("GuideNextDay", GUIDE_NEXT_DAY, 0x0D23), + DOM_KEY_MAP("GuidePreviousDay", GUIDE_PREVIOUS_DAY, 0x0D24), + DOM_KEY_MAP("Info", INFO, 0x0D25), + DOM_KEY_MAP("InstantReplay", INSTANT_REPLAY, 0x0D26), + DOM_KEY_MAP("Link", LINK, 0x0D27), + DOM_KEY_MAP("ListProgram", LIST_PROGRAM, 0x0D28), + DOM_KEY_MAP("LiveContent", LIVE_CONTENT, 0x0D29), + DOM_KEY_MAP("Lock", LOCK, 0x0D2A), + DOM_KEY_MAP("MediaApps", MEDIA_APPS, 0x0D2B), + DOM_KEY_MAP("MediaAudioTrack", MEDIA_AUDIO_TRACK, 0x0D50), + // Moved to Multimedia Keys section: + // ("MediaFastForward", MEDIA_FAST_FORWARD, 0x0D2C), + DOM_KEY_MAP("MediaLast", MEDIA_LAST, 0x0D2D), + // Moved to Multimedia Keys section: + // ("MediaPause", MEDIA_PAUSE, 0x0D2E), + // ("MediaPlay", MEDIA_PLAY, 0x0D2F), + // ("MediaRecord", MEDIA_RECORD, 0x0D30), + // ("MediaRewind", MEDIA_REWIND, 0x0D31), + DOM_KEY_MAP("MediaSkipBackward", MEDIA_SKIP_BACKWARD, 0x0D51), + DOM_KEY_MAP("MediaSkipForward", MEDIA_SKIP_FORWARD, 0x0D52), + DOM_KEY_MAP("MediaSkip", MEDIA_SKIP, 0x0D32), + DOM_KEY_MAP("MediaStepBackward", MEDIA_STEP_BACKWARD, 0x0D53), + DOM_KEY_MAP("MediaStepForward", MEDIA_STEP_FORWARD, 0x0D54), + DOM_KEY_MAP("MediaTopMenu", MEDIA_TOP_MENU, 0x0D55), + DOM_KEY_MAP("NavigateIn", NAVIGATE_IN, 0x0D56), + DOM_KEY_MAP("NavigateNext", NAVIGATE_NEXT, 0x0D57), + DOM_KEY_MAP("NavigateOut", NAVIGATE_OUT, 0x0D58), + DOM_KEY_MAP("NavigatePrevious", NAVIGATE_PREVIOUS, 0x0D59), + DOM_KEY_MAP("NextFavoriteChannel", NEXT_FAVORITE_CHANNEL, 0x0D33), + DOM_KEY_MAP("NextUserProfile", NEXT_USER_PROFILE, 0x0D34), + DOM_KEY_MAP("OnDemand", ON_DEMAND, 0x0D35), + DOM_KEY_MAP("Pairing", PAIRING, 0x0D5A), + DOM_KEY_MAP("PinPDown", PINP_DOWN, 0x0D36), + DOM_KEY_MAP("PinPMove", PINP_MOVE, 0x0D37), + DOM_KEY_MAP("PinPToggle", PINP_TOGGLE, 0x0D38), + DOM_KEY_MAP("PinPUp", PINP_UP, 0x0D39), + DOM_KEY_MAP("PlaySpeedDown", PLAY_SPEED_DOWN, 0x0D3A), + DOM_KEY_MAP("PlaySpeedReset", PLAY_SPEED_RESET, 0x0D3B), + DOM_KEY_MAP("PlaySpeedUp", PLAY_SPEED_UP, 0x0D3C), + DOM_KEY_MAP("RandomToggle", RANDOM_TOGGLE, 0x0D3D), + DOM_KEY_MAP("RcLowBattery", RC_LOW_BATTERY, 0x0D3E), + DOM_KEY_MAP("RecordSpeedNext", RECORD_SPEED_NEXT, 0x0D3F), + DOM_KEY_MAP("RfBypass", RF_BYPASS, 0x0D40), + DOM_KEY_MAP("ScanChannelsToggle", SCAN_CHANNELS_TOGGLE, 0x0D41), + DOM_KEY_MAP("ScreenModeNext", SCREEN_MODE_NEXT, 0x0D42), + DOM_KEY_MAP("Settings", SETTINGS, 0x0D43), + DOM_KEY_MAP("SplitScreenToggle", SPLIT_SCREEN_TOGGLE, 0x0D44), + DOM_KEY_MAP("STBInput", STB_INPUT, 0x0D45), + DOM_KEY_MAP("STBPower", STB_POWER, 0x0D46), + DOM_KEY_MAP("Subtitle", SUBTITLE, 0x0D47), + DOM_KEY_MAP("Teletext", TELETEXT, 0x0D48), + DOM_KEY_MAP("VideoModeNext", VIDEO_MODE_NEXT, 0x0D4C), + DOM_KEY_MAP("Wink", WINK, 0x0D4D), + DOM_KEY_MAP("ZoomToggle", ZOOM_TOGGLE, 0x0D4E), }; diff --git a/chromium/ui/events/keycodes/dom/keycode_converter.cc b/chromium/ui/events/keycodes/dom/keycode_converter.cc index b1373b4b8c7..4531acd128a 100644 --- a/chromium/ui/events/keycodes/dom/keycode_converter.cc +++ b/chromium/ui/events/keycodes/dom/keycode_converter.cc @@ -43,14 +43,10 @@ struct DomKeyMapEntry { #define DOM_KEY_MAP_DECLARATION const DomKeyMapEntry dom_key_map[] = #define DOM_KEY_UNI(key, id, value) {DomKey::id, key} -#define DOM_KEY_MAP_BEGIN -#define DOM_KEY_MAP(key, id) {DomKey::id, key} -#define DOM_KEY_MAP_END +#define DOM_KEY_MAP(key, id, value) {DomKey::id, key} #include "ui/events/keycodes/dom/dom_key_data.inc" #undef DOM_KEY_MAP_DECLARATION -#undef DOM_KEY_MAP_BEGIN #undef DOM_KEY_MAP -#undef DOM_KEY_MAP_END #undef DOM_KEY_UNI const size_t kDomKeyMapEntries = arraysize(dom_key_map); @@ -235,7 +231,6 @@ bool KeycodeConverter::IsDomKeyForModifier(DomKey dom_key) { case DomKey::HYPER: case DomKey::META: case DomKey::NUM_LOCK: - case DomKey::OS: case DomKey::SCROLL_LOCK: case DomKey::SHIFT: case DomKey::SUPER: diff --git a/chromium/ui/events/keycodes/dom/keycode_converter_data.inc b/chromium/ui/events/keycodes/dom/keycode_converter_data.inc index c46c8e8416c..bf0b47bdfb0 100644 --- a/chromium/ui/events/keycodes/dom/keycode_converter_data.inc +++ b/chromium/ui/events/keycodes/dom/keycode_converter_data.inc @@ -10,7 +10,7 @@ // [0] USB HID Usage Tables, // http://www.usb.org/developers/hidpage/Hut1_12v2.pdf // [1] DOM Level 3 KeyboardEvent code Values, -// http://www.w3.org/TR/DOM-Level-3-Events-code/ +// http://www.w3.org/TR/uievents-code/ // [2] OS X <HIToolbox/Events.h> // [3] Linux <linux/input.h> and hid-input.c // [4] USB HID to PS/2 Scan Code Translation Table @@ -54,6 +54,10 @@ // are distinct from UI Events' "ContextMenu", which corresponds to // USB 0x070065 [Keyboard Application] via evdev 0x7F KEY_COMPOSE, // following Windows convention.) +// +// [L3] Linux flattens both USB 0x070048 [Keyboard Pause] and 0x0C00B1 +// [Media Pause] to 0x77 KEY_PAUSE. We map the former, since [1] +// defines a 'Pause' code but no 'MediaPause' code. // Windows notes: // @@ -89,8 +93,7 @@ USB_KEYMAP_DECLARATION { USB_KEYMAP(0x000010, 0x0000, 0x0000, 0x0000, 0xffff, "Hyper", HYPER), USB_KEYMAP(0x000011, 0x0000, 0x0000, 0x0000, 0xffff, "Super", SUPER), USB_KEYMAP(0x000012, 0x0000, 0x0000, 0x0000, 0xffff, "Fn", FN), - // FLock is named FN_LOCK because F_LOCK conflicts with <fcntl.h> - USB_KEYMAP(0x000013, 0x0000, 0x0000, 0x0000, 0xffff, "FLock", FN_LOCK), + USB_KEYMAP(0x000013, 0x0000, 0x0000, 0x0000, 0xffff, "FnLock", FN_LOCK), USB_KEYMAP(0x000014, 0x0000, 0x0000, 0x0000, 0xffff, "Suspend", SUSPEND), USB_KEYMAP(0x000015, 0x0000, 0x0000, 0x0000, 0xffff, "Resume", RESUME), USB_KEYMAP(0x000016, 0x0000, 0x0000, 0x0000, 0xffff, "Turbo", TURBO), @@ -453,7 +456,9 @@ USB_KEYMAP_DECLARATION { // KEY_BRIGHTNESS* added in Linux 3.16 // http://www.usb.org/developers/hidpage/HUTRR41.pdf - // USB XKB Win Mac + // USB evdev XKB Win Mac Code + USB_KEYMAP(0x0c0060, 0x0166, 0x016e, 0x0000, 0xffff, NULL, INFO), + USB_KEYMAP(0x0c0061, 0x0172, 0x017a, 0x0000, 0xffff, NULL, CLOSED_CAPTION_TOGGLE), USB_KEYMAP(0x0c006f, 0x00e1, 0x00e9, 0x0000, 0xffff, "BrightnessUp", BRIGHTNESS_UP), USB_KEYMAP(0x0c0070, 0x00e0, 0x00e8, 0x0000, 0xffff, "BrightnessDown", BRIGHTNESS_DOWN), // Display Brightness Decrement @@ -461,14 +466,19 @@ USB_KEYMAP_DECLARATION { USB_KEYMAP(0x0c0073, 0x0250, 0x0258, 0x0000, 0xffff, NULL, BRIGHTNESS_MINIMIUM), USB_KEYMAP(0x0c0074, 0x0251, 0x0259, 0x0000, 0xffff, NULL, BRIGHTNESS_MAXIMUM), USB_KEYMAP(0x0c0075, 0x00f4, 0x00fc, 0x0000, 0xffff, NULL, BRIGHTNESS_AUTO), + USB_KEYMAP(0x0c0083, 0x0195, 0x019d, 0x0000, 0xffff, NULL, MEDIA_LAST), + USB_KEYMAP(0x0c008c, 0x00a9, 0x00b1, 0x0000, 0xffff, NULL, LAUNCH_PHONE), + USB_KEYMAP(0x0c008d, 0x016a, 0x0172, 0x0000, 0xffff, NULL, PROGRAM_GUIDE), + USB_KEYMAP(0x0c0094, 0x00ae, 0x00b6, 0x0000, 0xffff, NULL, EXIT), + USB_KEYMAP(0x0c009c, 0x019a, 0x01a2, 0x0000, 0xffff, NULL, CHANNEL_UP), + USB_KEYMAP(0x0c009d, 0x019b, 0x01a3, 0x0000, 0xffff, NULL, CHANNEL_DOWN), // USB evdev XKB Win Mac - //USB_KEYMAP(0x0c00b0, 0x00cf, 0x00d7, 0x????, 0x????, "MediaPlay", MEDIA_PLAY), - //USB_KEYMAP(0x0c00b1, 0x0077, 0x007f, 0x????, 0x????, "MediaPause", MEDIA_PAUSE), - //USB_KEYMAP(0x0c00b2, 0x00a7, 0x00af, 0x????, 0x????, "MediaRecord", MEDIA_RECORD), - //USB_KEYMAP(0x0c00b3, 0x00d0, 0x00d8, 0x????, 0x????, "MediaFastForward", - // MEDIA_FAST_FORWARD), - //USB_KEYMAP(0x0c00b4, 0x00a8, 0x00b0, 0x????, 0x????, "MediaRewind", MEDIA_REWIND), + USB_KEYMAP(0x0c00b0, 0x00cf, 0x00d7, 0x0000, 0xffff, "MediaPlay", MEDIA_PLAY), + //USB_KEYMAP(0x0c00b1, 0x0077, 0x007f, 0x0000, 0xffff, "MediaPause", MEDIA_PAUSE), + USB_KEYMAP(0x0c00b2, 0x00a7, 0x00af, 0x0000, 0xffff, "MediaRecord", MEDIA_RECORD), + USB_KEYMAP(0x0c00b3, 0x00d0, 0x00d8, 0x0000, 0xffff, "MediaFastForward", MEDIA_FAST_FORWARD), + USB_KEYMAP(0x0c00b4, 0x00a8, 0x00b0, 0x0000, 0xffff, "MediaRewind", MEDIA_REWIND), USB_KEYMAP(0x0c00b5, 0x00a3, 0x00ab, 0xe019, 0xffff, "MediaTrackNext", MEDIA_TRACK_NEXT), USB_KEYMAP(0x0c00b6, 0x00a5, 0x00ad, 0xe010, 0xffff, "MediaTrackPrevious", @@ -477,15 +487,25 @@ USB_KEYMAP_DECLARATION { USB_KEYMAP(0x0c00b8, 0x00a1, 0x00a9, 0xe02c, 0xffff, "Eject", EJECT), USB_KEYMAP(0x0c00cd, 0x00a4, 0x00ac, 0xe022, 0xffff, "MediaPlayPause", MEDIA_PLAY_PAUSE), - USB_KEYMAP(0x0c00cf, 0x0246, 0x024e, 0x0000, 0xffff, NULL, VOICE_COMMAND), + USB_KEYMAP(0x0c00cf, 0x0246, 0x024e, 0x0000, 0xffff, NULL, SPEECH_INPUT_TOGGLE), + USB_KEYMAP(0x0c00e5, 0x00d1, 0x00d9, 0x0000, 0xffff, NULL, BASS_BOOST), + //USB_KEYMAP(0x0c00e6, 0x0000, 0x0000, 0x0000, 0xffff, NULL, SURROUND_MODE), + //USB_KEYMAP(0x0c0150, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BALANCE_RIGHT), + //USB_KEYMAP(0x0c0151, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BALANCE_LEFT ), + //USB_KEYMAP(0x0c0152, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BASS_INCREMENT), + //USB_KEYMAP(0x0c0153, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BASS_DECREMENT), + //USB_KEYMAP(0x0c0154, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_TREBLE_INCREMENT), + //USB_KEYMAP(0x0c0155, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_TREBLE_DECREMENT), // USB#0c0183: AL Consumer Control Configuration USB_KEYMAP(0x0c0183, 0x00ab, 0x00b3, 0xe06d, 0xffff, "MediaSelect", MEDIA_SELECT), + USB_KEYMAP(0x0c0184, 0x01a5, 0x01ad, 0x0000, 0xffff, NULL, LAUNCH_WORD_PROCESSOR), + USB_KEYMAP(0x0c0186, 0x01a7, 0x01af, 0x0000, 0xffff, NULL, LAUNCH_SPREADSHEET), // USB#0x0c018a AL_EmailReader - USB_KEYMAP(0x0c018a, 0x009b, 0x018a, 0xe06c, 0xffff, "LaunchMail", LAUNCH_MAIL), + USB_KEYMAP(0x0c018a, 0x009b, 0x00a3, 0xe06c, 0xffff, "LaunchMail", LAUNCH_MAIL), // USB#0x0c018d: AL Contacts/Address Book - //USB_KEYMAP(0x0c018d, 0x01ad, 0x01b5, 0x0000, 0xffff, NULL, LAUNCH_CONTACTS), + USB_KEYMAP(0x0c018d, 0x01ad, 0x01b5, 0x0000, 0xffff, NULL, LAUNCH_CONTACTS), // USB#0x0c018e: AL Calendar/Schedule - //USB_KEYMAP(0x0c018e, 0x018d, 0x0195, 0x0000, 0xffff, NULL, LAUNCH_CALENDAR), + USB_KEYMAP(0x0c018e, 0x018d, 0x0195, 0x0000, 0xffff, NULL, LAUNCH_CALENDAR), // USB#0x0c018f AL Task/Project Manager //USB_KEYMAP(0x0c018f, 0x0241, 0x0249, 0x0000, 0xffff, NULL, LAUNCH_TASK_MANAGER), // USB#0x0c0190: AL Log/Journal/Timecard @@ -494,15 +514,17 @@ USB_KEYMAP_DECLARATION { USB_KEYMAP(0x0c0192, 0x008c, 0x0094, 0xe021, 0xffff, "LaunchApp2", LAUNCH_APP2), // USB#0c0194: My Computer (AL_LocalMachineBrowser) USB_KEYMAP(0x0c0194, 0x0090, 0x0098, 0xe06b, 0xffff, "LaunchApp1", LAUNCH_APP1), - //USB_KEYMAP(0x0c0196, 0x0096, 0x009e, 0x0000, 0xffff, NULL, LAUNCH_INTERNET_BROWSER), + USB_KEYMAP(0x0c0196, 0x0096, 0x009e, 0x0000, 0xffff, NULL, LAUNCH_INTERNET_BROWSER), + USB_KEYMAP(0x0c019C, 0x01b1, 0x01b9, 0x0000, 0xffff, NULL, LOG_OFF), // USB#0x0c019e: AL Terminal Lock/Screensaver USB_KEYMAP(0x0c019e, 0x0098, 0x00a0, 0x0000, 0xffff, NULL, LOCK_SCREEN), // USB#0x0c019f AL Control Panel - //USB_KEYMAP(0x0c019f, 0x0243, 0x024b, 0x0000, 0xffff, NULL, LAUNCH_CONTROL_PANEL), + USB_KEYMAP(0x0c019f, 0x0243, 0x024b, 0x0000, 0xffff, NULL, LAUNCH_CONTROL_PANEL), // USB#0x0c01a2: AL Select Task/Application USB_KEYMAP(0x0c01a2, 0x0244, 0x024c, 0x0000, 0xffff, "SelectTask", SELECT_TASK), // USB#0x0c01a7: AL_Documents USB_KEYMAP(0x0c01a7, 0x00eb, 0x00f3, 0x0000, 0xffff, NULL, LAUNCH_DOCUMENTS), + USB_KEYMAP(0x0c01ab, 0x01b0, 0x01b8, 0x0000, 0xffff, NULL, SPELL_CHECK), // USB#0x0c01ae: AL Keyboard Layout USB_KEYMAP(0x0c01ae, 0x0176, 0x017e, 0x0000, 0xffff, NULL, LAUNCH_KEYBOARD_LAYOUT), USB_KEYMAP(0x0c01b1, 0x0245, 0x024d, 0x0000, 0xffff, "LaunchScreenSaver", @@ -510,9 +532,15 @@ USB_KEYMAP_DECLARATION { // USB#0c01b4: Home Directory (AL_FileBrowser) (Explorer) //USB_KEYMAP(0x0c01b4, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LAUNCH_FILE_BROWSER), // USB#0x0c01b7: AL Audio Browser - //USB_KEYMAP(0x0c01b7, 0x0188, 0x0190, 0x0000, 0xffff, NULL, LAUNCH_AUDIO_BROWSER), + USB_KEYMAP(0x0c01b7, 0x0188, 0x0190, 0x0000, 0xffff, NULL, LAUNCH_AUDIO_BROWSER), + // USB#0x0c0201: AC New + USB_KEYMAP(0x0c0201, 0x00b5, 0x00bd, 0x0000, 0xffff, NULL, NEW), + // USB#0x0c0203: AC Close + USB_KEYMAP(0x0c0203, 0x00ce, 0x00d6, 0x0000, 0xffff, NULL, CLOSE), + // USB#0x0c0207: AC Close + USB_KEYMAP(0x0c0207, 0x00ea, 0x00f2, 0x0000, 0xffff, NULL, SAVE), // USB#0x0c0208: AC Print - //USB_KEYMAP(0x0c0208, 0x00d2, 0x00da, 0x0000, 0xffff, NULL, PRINT), + USB_KEYMAP(0x0c0208, 0x00d2, 0x00da, 0x0000, 0xffff, NULL, PRINT), // USB#0x0c0221: AC_Search USB_KEYMAP(0x0c0221, 0x00d9, 0x00e1, 0xe065, 0xffff, "BrowserSearch", BROWSER_SEARCH), // USB#0x0c0223: AC_Home @@ -530,12 +558,16 @@ USB_KEYMAP_DECLARATION { // USB#0x0c022a: AC_Bookmarks (Favorites) USB_KEYMAP(0x0c022a, 0x009c, 0x00a4, 0xe066, 0xffff, "BrowserFavorites", BROWSER_FAVORITES), + USB_KEYMAP(0x0c022d, 0x01a2, 0x01aa, 0x0000, 0xffff, NULL, ZOOM_IN), + USB_KEYMAP(0x0c022e, 0x01a3, 0x01ab, 0x0000, 0xffff, NULL, ZOOM_OUT), // USB#0x0c0230: AC Full Screen View //USB_KEYMAP(0x0c0230, 0x0000, 0x0000, 0x0000, 0xffff, NULL, ZOOM_FULL), // USB#0x0c0231: AC Normal View //USB_KEYMAP(0x0c0231, 0x0000, 0x0000, 0x0000, 0xffff, NULL, ZOOM_NORMAL), // USB#0x0c0232: AC View Toggle USB_KEYMAP(0x0c0232, 0x0000, 0x0000, 0x0000, 0xffff, "ZoomToggle", ZOOM_TOGGLE), + // USB#0x0c0279: AC Redo/Repeat + USB_KEYMAP(0x0c0279, 0x00b6, 0x00be, 0x0000, 0xffff, NULL, REDO), // USB#0x0c0289: AC_Reply USB_KEYMAP(0x0c0289, 0x00e8, 0x00f0, 0x0000, 0xffff, "MailReply", MAIL_REPLY), // USB#0x0c028b: AC_ForwardMsg (MailForward) diff --git a/chromium/ui/events/keycodes/dom/keycode_converter_unittest.cc b/chromium/ui/events/keycodes/dom/keycode_converter_unittest.cc index 68e2864e1bb..b6bab477ac9 100644 --- a/chromium/ui/events/keycodes/dom/keycode_converter_unittest.cc +++ b/chromium/ui/events/keycodes/dom/keycode_converter_unittest.cc @@ -8,6 +8,7 @@ #include <stdint.h> #include <map> +#include <set> #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -18,20 +19,93 @@ using ui::KeycodeConverter; namespace { -#if defined(OS_WIN) -const size_t kExpectedMappedKeyCount = 157; -#elif defined(OS_LINUX) || defined(OS_ANDROID) -const size_t kExpectedMappedKeyCount = 178; -#elif defined(OS_MACOSX) -const size_t kExpectedMappedKeyCount = 118; -#else -const size_t kExpectedMappedKeyCount = 0; -#endif +// Number of native codes expected to be mapped for each kind of native code. +// These are in the same order as the columns in keycode_converter_data.inc +// as reflected in the USB_KEYMAP() macro below. +const size_t expected_mapped_key_count[] = { + 207, // evdev + 207, // xkb + 157, // windows + 118, // mac +}; + +const size_t kNativeColumns = arraysize(expected_mapped_key_count); + +struct KeycodeConverterData { + uint32_t usb_keycode; + const char* code; + const char* id; + int native_keycode[kNativeColumns]; +}; + +#define USB_KEYMAP(usb, evdev, xkb, win, mac, code, id) \ + { usb, code, #id, { evdev, xkb, win, mac } } +#define USB_KEYMAP_DECLARATION \ + const KeycodeConverterData kKeycodeConverterData[] = +#include "ui/events/keycodes/dom/keycode_converter_data.inc" +#undef USB_KEYMAP +#undef USB_KEYMAP_DECLARATION const uint32_t kUsbNonExistentKeycode = 0xffffff; const uint32_t kUsbUsBackslash = 0x070031; const uint32_t kUsbNonUsHash = 0x070032; +TEST(UsbKeycodeMap, KeycodeConverterData) { + // This test looks at all kinds of supported native codes. + // Verify that there are no duplicate entries in the mapping. + std::map<uint32_t, uint16_t> usb_to_native[kNativeColumns]; + std::map<uint16_t, uint32_t> native_to_usb[kNativeColumns]; + int invalid_native_keycode[kNativeColumns]; + for (size_t i = 0; i < kNativeColumns; ++i) { + invalid_native_keycode[i] = kKeycodeConverterData[0].native_keycode[i]; + } + for (const auto& it : kKeycodeConverterData) { + SCOPED_TRACE(it.id); + for (size_t i = 0; i < kNativeColumns; ++i) { + if (it.native_keycode[i] == invalid_native_keycode[i]) + continue; + // Verify that the USB or native codes aren't duplicated. + EXPECT_EQ(0U, usb_to_native[i].count(it.usb_keycode)) + << " duplicate of USB code 0x" << std::hex << std::setfill('0') + << std::setw(6) << it.usb_keycode + << " to native 0x" + << std::setw(4) << it.native_keycode[i] + << " (previous was 0x" + << std::setw(4) << usb_to_native[i][it.usb_keycode] + << ")"; + usb_to_native[i][it.usb_keycode] = it.native_keycode[i]; + EXPECT_EQ(0U, native_to_usb[i].count(it.native_keycode[i])) + << " duplicate of native code 0x" << std::hex << std::setfill('0') + << std::setw(4) << it.native_keycode[i] + << " to USB 0x" + << std::setw(6) << it.usb_keycode + << " (previous was 0x" + << std::setw(6) << native_to_usb[i][it.native_keycode[i]] + << ")"; + native_to_usb[i][it.native_keycode[i]] = it.usb_keycode; + } + } + // Verify that the number of mapped keys is what we expect, i.e. we haven't + // lost any, and if we've added some then the expectation has been updated. + for (size_t i = 0; i < kNativeColumns; ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(expected_mapped_key_count[i], usb_to_native[i].size()); + } +} + +TEST(UsbKeycodeMap, EvdevXkb) { + // XKB codes on a Linux system are 8 plus the corresponding evdev code. + // Verify that this relationship holds in the keycode converter data. + for (const auto& it : kKeycodeConverterData) { + SCOPED_TRACE(it.id); + int evdev_code = it.native_keycode[0]; + int xkb_code = it.native_keycode[1]; + if (evdev_code || xkb_code) { + EXPECT_EQ(xkb_code, evdev_code + 8); + } + } +} + TEST(UsbKeycodeMap, Basic) { // Verify that the first element in the table is the "invalid" code. const ui::KeycodeMapEntry* keycode_map = @@ -43,9 +117,6 @@ TEST(UsbKeycodeMap, Basic) { EXPECT_EQ(ui::KeycodeConverter::InvalidNativeKeycode(), ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::NONE)); - // Verify that there are no duplicate entries in the mapping. - std::map<uint32_t, uint16_t> usb_to_native; - std::map<uint16_t, uint32_t> native_to_usb; size_t numEntries = ui::KeycodeConverter::NumKeycodeMapEntriesForTest(); for (size_t i = 0; i < numEntries; ++i) { const ui::KeycodeMapEntry* entry = &keycode_map[i]; @@ -65,32 +136,7 @@ TEST(UsbKeycodeMap, Basic) { EXPECT_EQ(entry->native_keycode, ui::KeycodeConverter::DomCodeToNativeKeycode(dom_code)); } - - // Verify that the USB or native codes aren't duplicated. - EXPECT_EQ(0U, usb_to_native.count(entry->usb_keycode)) - << " duplicate of USB code 0x" << std::hex << std::setfill('0') - << std::setw(6) << entry->usb_keycode - << " to native 0x" - << std::setw(4) << entry->native_keycode - << " (previous was 0x" - << std::setw(4) << usb_to_native[entry->usb_keycode] - << ")"; - usb_to_native[entry->usb_keycode] = entry->native_keycode; - EXPECT_EQ(0U, native_to_usb.count(entry->native_keycode)) - << " duplicate of native code 0x" << std::hex << std::setfill('0') - << std::setw(4) << entry->native_keycode - << " to USB 0x" - << std::setw(6) << entry->usb_keycode - << " (previous was 0x" - << std::setw(6) << native_to_usb[entry->native_keycode] - << ")"; - native_to_usb[entry->native_keycode] = entry->usb_keycode; } - ASSERT_EQ(usb_to_native.size(), native_to_usb.size()); - - // Verify that the number of mapped keys is what we expect, i.e. we haven't - // lost any, and if we've added some then the expectation has been updated. - EXPECT_EQ(kExpectedMappedKeyCount, usb_to_native.size()); } TEST(UsbKeycodeMap, NonExistent) { @@ -180,7 +226,9 @@ TEST(KeycodeConverter, DomKey) { EXPECT_STREQ(test.string, s.c_str()); } } - // Round-trip test all UI Events KeyboardEvent.key strings. + // Round-trip test all UI Events KeyboardEvent.key strings, and check + // that encodings are distinct. + std::set<ui::DomKey::Base> keys; const char* s = nullptr; for (size_t i = 0; (s = ui::KeycodeConverter::DomKeyStringForTest(i)) != nullptr; ++i) { @@ -188,6 +236,11 @@ TEST(KeycodeConverter, DomKey) { ui::DomKey key = ui::KeycodeConverter::KeyStringToDomKey(s); if (s) { EXPECT_STREQ(s, ui::KeycodeConverter::DomKeyToKeyString(key).c_str()); + if (keys.count(key) == 0) { + keys.insert(key); + } else { + ADD_FAILURE() << "duplicate encoding:" << key; + } } else { EXPECT_EQ(ui::DomKey::NONE, key); } diff --git a/chromium/ui/events/keycodes/dom_us_layout_data.h b/chromium/ui/events/keycodes/dom_us_layout_data.h index 3ed0d241ddd..b0e2646ad9b 100644 --- a/chromium/ui/events/keycodes/dom_us_layout_data.h +++ b/chromium/ui/events/keycodes/dom_us_layout_data.h @@ -105,6 +105,7 @@ const struct NonPrintableCodeEntry { {DomCode::ARROW_RIGHT, DomKey::ARROW_RIGHT}, {DomCode::ARROW_UP, DomKey::ARROW_UP}, {DomCode::BACKSPACE, DomKey::BACKSPACE}, + {DomCode::BASS_BOOST, DomKey::AUDIO_BASS_BOOST_TOGGLE}, {DomCode::BRIGHTNESS_DOWN, DomKey::BRIGHTNESS_DOWN}, {DomCode::BRIGHTNESS_UP, DomKey::BRIGHTNESS_UP}, // {DomCode::BRIGHTNESS_AUTO, DomKey::_} @@ -119,6 +120,10 @@ const struct NonPrintableCodeEntry { {DomCode::BROWSER_SEARCH, DomKey::BROWSER_SEARCH}, {DomCode::BROWSER_STOP, DomKey::BROWSER_STOP}, {DomCode::CAPS_LOCK, DomKey::CAPS_LOCK}, + {DomCode::CHANNEL_DOWN, DomKey::CHANNEL_DOWN}, + {DomCode::CHANNEL_UP, DomKey::CHANNEL_UP}, + {DomCode::CLOSE, DomKey::CLOSE}, + {DomCode::CLOSED_CAPTION_TOGGLE, DomKey::CLOSED_CAPTION_TOGGLE}, {DomCode::CONTEXT_MENU, DomKey::CONTEXT_MENU}, {DomCode::CONTROL_LEFT, DomKey::CONTROL}, {DomCode::CONTROL_RIGHT, DomKey::CONTROL}, @@ -130,6 +135,7 @@ const struct NonPrintableCodeEntry { {DomCode::END, DomKey::END}, {DomCode::ENTER, DomKey::ENTER}, {DomCode::ESCAPE, DomKey::ESCAPE}, + {DomCode::EXIT, DomKey::EXIT}, {DomCode::F1, DomKey::F1}, {DomCode::F2, DomKey::F2}, {DomCode::F3, DomKey::F3}, @@ -160,6 +166,7 @@ const struct NonPrintableCodeEntry { {DomCode::HELP, DomKey::HELP}, {DomCode::HOME, DomKey::HOME}, {DomCode::HYPER, DomKey::HYPER}, + {DomCode::INFO, DomKey::INFO}, {DomCode::INSERT, DomKey::INSERT}, // {DomCode::INTL_RO, DomKey::_} {DomCode::KANA_MODE, DomKey::KANA_MODE}, @@ -170,21 +177,36 @@ const struct NonPrintableCodeEntry { {DomCode::LANG5, DomKey::ZENKAKU_HANKAKU}, {DomCode::LAUNCH_APP1, DomKey::LAUNCH_MY_COMPUTER}, {DomCode::LAUNCH_APP2, DomKey::LAUNCH_CALCULATOR}, + {DomCode::LAUNCH_AUDIO_BROWSER, DomKey::LAUNCH_MUSIC_PLAYER}, + {DomCode::LAUNCH_CALENDAR, DomKey::LAUNCH_CALENDAR}, + {DomCode::LAUNCH_CONTACTS, DomKey::LAUNCH_CONTACTS}, + {DomCode::LAUNCH_CONTROL_PANEL, DomKey::SETTINGS}, + {DomCode::LAUNCH_INTERNET_BROWSER, DomKey::LAUNCH_WEB_BROWSER}, {DomCode::LAUNCH_MAIL, DomKey::LAUNCH_MAIL}, + {DomCode::LAUNCH_PHONE, DomKey::LAUNCH_PHONE}, {DomCode::LAUNCH_SCREEN_SAVER, DomKey::LAUNCH_SCREEN_SAVER}, + {DomCode::LAUNCH_SPREADSHEET, DomKey::LAUNCH_SPREADSHEET}, // {DomCode::LAUNCH_DOCUMENTS, DomKey::_} // {DomCode::LAUNCH_FILE_BROWSER, DomKey::_} // {DomCode::LAUNCH_KEYBOARD_LAYOUT, DomKey::_} {DomCode::LOCK_SCREEN, DomKey::LAUNCH_SCREEN_SAVER}, + {DomCode::LOG_OFF, DomKey::LOG_OFF}, {DomCode::MAIL_FORWARD, DomKey::MAIL_FORWARD}, {DomCode::MAIL_REPLY, DomKey::MAIL_REPLY}, {DomCode::MAIL_SEND, DomKey::MAIL_SEND}, + {DomCode::MEDIA_FAST_FORWARD, DomKey::MEDIA_FAST_FORWARD}, + {DomCode::MEDIA_LAST, DomKey::MEDIA_LAST}, + // {DomCode::MEDIA_PAUSE, DomKey::MEDIA_PAUSE}, + {DomCode::MEDIA_PLAY, DomKey::MEDIA_PLAY}, {DomCode::MEDIA_PLAY_PAUSE, DomKey::MEDIA_PLAY_PAUSE}, - {DomCode::MEDIA_SELECT, DomKey::MEDIA_SELECT}, + {DomCode::MEDIA_RECORD, DomKey::MEDIA_RECORD}, + {DomCode::MEDIA_REWIND, DomKey::MEDIA_REWIND}, + {DomCode::MEDIA_SELECT, DomKey::LAUNCH_MEDIA_PLAYER}, {DomCode::MEDIA_STOP, DomKey::MEDIA_STOP}, {DomCode::MEDIA_TRACK_NEXT, DomKey::MEDIA_TRACK_NEXT}, {DomCode::MEDIA_TRACK_PREVIOUS, DomKey::MEDIA_TRACK_PREVIOUS}, // {DomCode::MENU, DomKey::_} + {DomCode::NEW, DomKey::NEW}, {DomCode::NON_CONVERT, DomKey::NON_CONVERT}, {DomCode::NUM_LOCK, DomKey::NUM_LOCK}, {DomCode::NUMPAD_BACKSPACE, DomKey::BACKSPACE}, @@ -197,28 +219,35 @@ const struct NonPrintableCodeEntry { // {DomCode::NUMPAD_MEMORY_STORE, DomKey::_} // {DomCode::NUMPAD_MEMORY_SUBTRACT, DomKey::_} {DomCode::OPEN, DomKey::OPEN}, - {DomCode::OS_LEFT, DomKey::OS}, - {DomCode::OS_RIGHT, DomKey::OS}, + {DomCode::OS_LEFT, DomKey::META}, + {DomCode::OS_RIGHT, DomKey::META}, {DomCode::PAGE_DOWN, DomKey::PAGE_DOWN}, {DomCode::PAGE_UP, DomKey::PAGE_UP}, {DomCode::PASTE, DomKey::PASTE}, {DomCode::PAUSE, DomKey::PAUSE}, {DomCode::POWER, DomKey::POWER}, + {DomCode::PRINT, DomKey::PRINT}, {DomCode::PRINT_SCREEN, DomKey::PRINT_SCREEN}, + {DomCode::PROGRAM_GUIDE, DomKey::GUIDE}, {DomCode::PROPS, DomKey::PROPS}, + {DomCode::REDO, DomKey::REDO}, + {DomCode::SAVE, DomKey::SAVE}, {DomCode::SCROLL_LOCK, DomKey::SCROLL_LOCK}, {DomCode::SELECT, DomKey::SELECT}, - // {DomCode::SELECT_TASK, DomKey::_} + {DomCode::SELECT_TASK, DomKey::APP_SWITCH}, {DomCode::SHIFT_LEFT, DomKey::SHIFT}, {DomCode::SHIFT_RIGHT, DomKey::SHIFT}, + {DomCode::SPEECH_INPUT_TOGGLE, DomKey::SPEECH_INPUT_TOGGLE}, + {DomCode::SPELL_CHECK, DomKey::SPELL_CHECK}, {DomCode::SUPER, DomKey::SUPER}, {DomCode::TAB, DomKey::TAB}, {DomCode::UNDO, DomKey::UNDO}, - // {DomCode::VOICE_COMMAND, DomKey::_} - {DomCode::VOLUME_DOWN, DomKey::VOLUME_DOWN}, - {DomCode::VOLUME_MUTE, DomKey::VOLUME_MUTE}, - {DomCode::VOLUME_UP, DomKey::VOLUME_UP}, + {DomCode::VOLUME_DOWN, DomKey::AUDIO_VOLUME_DOWN}, + {DomCode::VOLUME_MUTE, DomKey::AUDIO_VOLUME_MUTE}, + {DomCode::VOLUME_UP, DomKey::AUDIO_VOLUME_UP}, {DomCode::WAKE_UP, DomKey::WAKE_UP}, + {DomCode::ZOOM_IN, DomKey::ZOOM_IN}, + {DomCode::ZOOM_OUT, DomKey::ZOOM_OUT}, {DomCode::ZOOM_TOGGLE, DomKey::ZOOM_TOGGLE}, }; @@ -239,13 +268,12 @@ const struct DomKeyToKeyboardCodeEntry { {DomKey::CAPS_LOCK, VKEY_CAPITAL}, {DomKey::CONTROL, VKEY_CONTROL}, {DomKey::NUM_LOCK, VKEY_NUMLOCK}, - {DomKey::OS, VKEY_LWIN}, + {DomKey::META, VKEY_LWIN}, {DomKey::SCROLL_LOCK, VKEY_SCROLL}, {DomKey::SHIFT, VKEY_SHIFT}, // Whitespace Keys // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-whitespace {DomKey::ENTER, VKEY_RETURN}, - {DomKey::SEPARATOR, VKEY_SEPARATOR}, {DomKey::TAB, VKEY_TAB}, // Navigation Keys // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-navigation @@ -334,18 +362,18 @@ const struct DomKeyToKeyboardCodeEntry { // Multimedia Keys // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-multimedia {DomKey::MEDIA_PLAY_PAUSE, VKEY_MEDIA_PLAY_PAUSE}, - {DomKey::MEDIA_SELECT, VKEY_MEDIA_LAUNCH_MEDIA_SELECT}, {DomKey::MEDIA_STOP, VKEY_MEDIA_STOP}, {DomKey::MEDIA_TRACK_NEXT, VKEY_MEDIA_NEXT_TRACK}, {DomKey::MEDIA_TRACK_PREVIOUS, VKEY_MEDIA_PREV_TRACK}, {DomKey::PRINT, VKEY_PRINT}, - {DomKey::VOLUME_DOWN, VKEY_VOLUME_DOWN}, - {DomKey::VOLUME_MUTE, VKEY_VOLUME_MUTE}, - {DomKey::VOLUME_UP, VKEY_VOLUME_UP}, + {DomKey::AUDIO_VOLUME_DOWN, VKEY_VOLUME_DOWN}, + {DomKey::AUDIO_VOLUME_MUTE, VKEY_VOLUME_MUTE}, + {DomKey::AUDIO_VOLUME_UP, VKEY_VOLUME_UP}, // Application Keys // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-apps {DomKey::LAUNCH_CALCULATOR, VKEY_MEDIA_LAUNCH_APP2}, {DomKey::LAUNCH_MAIL, VKEY_MEDIA_LAUNCH_MAIL}, + {DomKey::LAUNCH_MEDIA_PLAYER, VKEY_MEDIA_LAUNCH_MEDIA_SELECT}, {DomKey::LAUNCH_MY_COMPUTER, VKEY_MEDIA_LAUNCH_APP1}, // Browser Keys // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-browser diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_android.cc b/chromium/ui/events/keycodes/keyboard_code_conversion_android.cc index e176a2bfe48..92a9f52124e 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_android.cc +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_android.cc @@ -75,9 +75,9 @@ DomKey GetDomKeyFromAndroidKeycode(int keycode) { case AKEYCODE_DPAD_RIGHT: return DomKey::ARROW_RIGHT; case AKEYCODE_VOLUME_UP: - return DomKey::VOLUME_UP; + return DomKey::AUDIO_VOLUME_UP; case AKEYCODE_VOLUME_DOWN: - return DomKey::VOLUME_DOWN; + return DomKey::AUDIO_VOLUME_DOWN; case AKEYCODE_POWER: return DomKey::POWER; case AKEYCODE_CAMERA: @@ -119,7 +119,7 @@ DomKey GetDomKeyFromAndroidKeycode(int keycode) { case AKEYCODE_MEDIA_FAST_FORWARD: return DomKey::MEDIA_FAST_FORWARD; case AKEYCODE_MUTE: - return DomKey::VOLUME_MUTE; + return DomKey::AUDIO_VOLUME_MUTE; case AKEYCODE_PAGE_UP: return DomKey::PAGE_UP; case AKEYCODE_PAGE_DOWN: @@ -189,7 +189,7 @@ DomKey GetDomKeyFromAndroidKeycode(int keycode) { case AKEYCODE_NUM_LOCK: return DomKey::NUM_LOCK; case AKEYCODE_VOLUME_MUTE: - return DomKey::VOLUME_MUTE; + return DomKey::AUDIO_VOLUME_MUTE; case AKEYCODE_INFO: return DomKey::INFO; case AKEYCODE_CHANNEL_UP: @@ -201,7 +201,7 @@ DomKey GetDomKeyFromAndroidKeycode(int keycode) { case AKEYCODE_ZOOM_OUT: return DomKey::ZOOM_OUT; case AKEYCODE_TV: - return DomKey::T_V; + return DomKey::TV; case AKEYCODE_GUIDE: return DomKey::GUIDE; case AKEYCODE_CAPTIONS: diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm index f435f4ed2b9..8aae1d92eb4 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm @@ -9,6 +9,8 @@ #import <Carbon/Carbon.h> #include "base/logging.h" +#include "base/mac/mac_logging.h" +#include "base/mac/scoped_cftyperef.h" #include "base/macros.h" #include "ui/events/keycodes/dom/keycode_converter.h" @@ -16,6 +18,9 @@ namespace ui { namespace { +// This value is not defined but shows up as 0x36. +const int kVK_RightCommand = 0x36; + // A struct to hold a Windows keycode to Mac virtual keycode mapping. struct KeyCodeMap { KeyboardCode keycode; @@ -106,8 +111,8 @@ const KeyCodeMap kKeyCodesMap[] = { { VKEY_Y /* 0x59 */, kVK_ANSI_Y, 'y' }, { VKEY_Z /* 0x5A */, kVK_ANSI_Z, 'z' }, { VKEY_LWIN /* 0x5B */, kVK_Command, 0 }, - { VKEY_RWIN /* 0x5C */, 0x36, 0 }, - { VKEY_APPS /* 0x5D */, 0x36, 0 }, + { VKEY_RWIN /* 0x5C */, kVK_RightCommand, 0 }, + { VKEY_APPS /* 0x5D */, kVK_RightCommand, 0 }, { VKEY_SLEEP /* 0x5F */, -1, 0 }, { VKEY_NUMPAD0 /* 0x60 */, kVK_ANSI_Keypad0, '0' }, { VKEY_NUMPAD1 /* 0x61 */, kVK_ANSI_Keypad1, '1' }, @@ -439,6 +444,7 @@ KeyboardCode KeyboardCodeFromKeyCode(unsigned short keyCode) { DomKey DomKeyFromKeyCode(unsigned short keyCode) { switch (keyCode) { + case kVK_ANSI_KeypadEnter: case kVK_Return: return DomKey::ENTER; case kVK_Tab: @@ -448,6 +454,7 @@ DomKey DomKeyFromKeyCode(unsigned short keyCode) { case kVK_Escape: return DomKey::ESCAPE; case kVK_Command: + case kVK_RightCommand: return DomKey::META; case kVK_Shift: case kVK_RightShift: @@ -463,11 +470,11 @@ DomKey DomKeyFromKeyCode(unsigned short keyCode) { case kVK_Function: return DomKey::FN; case kVK_VolumeUp: - return DomKey::VOLUME_UP; + return DomKey::AUDIO_VOLUME_UP; case kVK_VolumeDown: - return DomKey::VOLUME_DOWN; + return DomKey::AUDIO_VOLUME_DOWN; case kVK_Mute: - return DomKey::VOLUME_MUTE; + return DomKey::AUDIO_VOLUME_MUTE; case kVK_F1: return DomKey::F1; case kVK_F2: @@ -535,11 +542,6 @@ DomKey DomKeyFromKeyCode(unsigned short keyCode) { DomKey DomKeyFromCharCode(unichar char_code) { switch (char_code) { - case 0x03: - return DomKey::ENTER; // Numpad Enter - // Mac maps backspace to forward delete unicode. - case 0x7f: - return DomKey::BACKSPACE; case NSUpArrowFunctionKey: return DomKey::ARROW_UP; case NSDownArrowFunctionKey: @@ -635,6 +637,56 @@ DomKey DomKeyFromCharCode(unichar char_code) { } } +UniChar MacKeycodeAndModifiersToCharacter(unsigned short mac_keycode, + int modifiers, + bool* is_dead_key) { + // Convert NSEvent modifiers to format UCKeyTranslate accepts. See docs + // on UCKeyTranslate for more info. + int unicode_modifiers = 0; + if (modifiers & NSShiftKeyMask) + unicode_modifiers |= shiftKey; + if (modifiers & NSAlphaShiftKeyMask) + unicode_modifiers |= alphaLock; + // if (modifiers & NSControlKeyMask) + // unicode_modifiers |= controlKey; + if (modifiers & NSAlternateKeyMask) + unicode_modifiers |= optionKey; + // if (modifiers & NSCommandKeyMask) + // unicode_modifiers |= cmdKey; + UInt32 modifier_key_state = (unicode_modifiers >> 8) & 0xFF; + + base::ScopedCFTypeRef<TISInputSourceRef> input_source_copy( + TISCopyCurrentKeyboardLayoutInputSource()); + CFDataRef layout_data = static_cast<CFDataRef>(TISGetInputSourceProperty( + input_source_copy, kTISPropertyUnicodeKeyLayoutData)); + const UCKeyboardLayout* layout = + reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(layout_data)); + + UInt32 dead_key_state = 0; + UniCharCount char_count = 0; + // According Apple's doc UCKeyTranslate::maxStringLength maybe up to 255 but + // would actually be rare to get more than 4. + UniChar unicode_string[4]; + OSStatus status = + UCKeyTranslate(layout, static_cast<UInt16>(mac_keycode), kUCKeyActionDown, + modifier_key_state, LMGetKbdLast(), 0, &dead_key_state, + arraysize(unicode_string), &char_count, unicode_string); + + OSSTATUS_DCHECK(status == noErr, status); + *is_dead_key = dead_key_state != 0; + if (*is_dead_key) { + // A dead key, injecting space to get the diacritic in an isolated form. + status = UCKeyTranslate(layout, static_cast<UInt16>(kVK_Space), + kUCKeyActionDown, 0, LMGetKbdLast(), 0, + &dead_key_state, arraysize(unicode_string), + &char_count, unicode_string); + OSSTATUS_DCHECK(status == noErr, status); + } + + // TODO(chongz): Handle multiple character case. Should be rare. + return unicode_string[0]; +} + } // namespace int MacKeyCodeForWindowsKeyCode(KeyboardCode keycode, @@ -742,12 +794,49 @@ DomCode DomCodeFromNSEvent(NSEvent* event) { DomKey DomKeyFromNSEvent(NSEvent* event) { // Apply the lookup based on the character first since that has the - // Keyboard layout and modifers already applied; whereas the keyCode + // Keyboard layout and modifiers already applied; whereas the keyCode // doesn't. if ([event type] == NSKeyDown || [event type] == NSKeyUp) { + // Cannot use [event characters] to check whether it's a dead key, because + // KeyUp event has the character form of the dead key in [event characters]. + bool is_dead_key = false; + // MacKeycodeAndModifiersToCharacter() is efficient (around 6E-4 ms). + unichar dead_dom_key_char = MacKeycodeAndModifiersToCharacter( + [event keyCode], [event modifierFlags], &is_dead_key); + if (is_dead_key) + return DomKey::DeadKeyFromCombiningCharacter(dead_dom_key_char); + + // [event characters] will have dead key state applied. NSString* characters = [event characters]; - if ([characters length] > 0) - return DomKeyFromCharCode([characters characterAtIndex:0]); + if ([characters length] > 0) { + // An invalid dead key combination will produce two characters, according + // to spec DomKey should be the last character. + // 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 + bool unused_is_dead_key; + const int kAllowedModifiersMask = + NSShiftKeyMask | NSAlphaShiftKeyMask | NSAlternateKeyMask; + // MacKeycodeAndModifiersToCharacter() is efficient (around 6E-4 ms). + dom_key_char = MacKeycodeAndModifiersToCharacter( + [event keyCode], [event modifierFlags] & kAllowedModifiersMask, + &unused_is_dead_key); + } + if (!std::iscntrl(dom_key_char)) + return DomKeyFromCharCode(dom_key_char); + } } return DomKeyFromKeyCode([event keyCode]); } diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_unittest.cc b/chromium/ui/events/keycodes/keyboard_code_conversion_unittest.cc index dab5faca4cf..92540fa1611 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_unittest.cc +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_unittest.cc @@ -246,11 +246,11 @@ TEST(KeyboardCodeConversion, ControlCharacters) { {true, ui::DomKey::ALT, ui::VKEY_MENU}, {true, ui::DomKey::ALT, ui::VKEY_MENU}}, {ui::DomCode::OS_LEFT, - {true, ui::DomKey::OS, ui::VKEY_LWIN}, - {true, ui::DomKey::OS, ui::VKEY_LWIN}}, + {true, ui::DomKey::META, ui::VKEY_LWIN}, + {true, ui::DomKey::META, ui::VKEY_LWIN}}, {ui::DomCode::OS_RIGHT, - {true, ui::DomKey::OS, ui::VKEY_LWIN}, - {true, ui::DomKey::OS, ui::VKEY_LWIN}}, + {true, ui::DomKey::META, ui::VKEY_LWIN}, + {true, ui::DomKey::META, ui::VKEY_LWIN}}, {ui::DomCode::DIGIT1, {true, ui::DomKey::Constant<'1'>::Character, ui::VKEY_1}, {true, ui::DomKey::Constant<'!'>::Character, ui::VKEY_1}}, @@ -264,8 +264,8 @@ TEST(KeyboardCodeConversion, ControlCharacters) { {true, ui::DomKey::F1, ui::VKEY_F1}, {true, ui::DomKey::F1, ui::VKEY_F1}}, {ui::DomCode::VOLUME_UP, - {true, ui::DomKey::VOLUME_UP, ui::VKEY_VOLUME_UP}, - {true, ui::DomKey::VOLUME_UP, ui::VKEY_VOLUME_UP}}, + {true, ui::DomKey::AUDIO_VOLUME_UP, ui::VKEY_VOLUME_UP}, + {true, ui::DomKey::AUDIO_VOLUME_UP, ui::VKEY_VOLUME_UP}}, }; for (const auto& it : kNonControlCharacters) { // Verify |DomCodeToControlCharacter()|. diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_xkb.cc b/chromium/ui/events/keycodes/keyboard_code_conversion_xkb.cc index a0bc96ab45d..e6817e18a40 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_xkb.cc +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_xkb.cc @@ -214,7 +214,7 @@ DomKey NonPrintableXKeySymToDomKey(xkb_keysym_t keysym) { #endif // defined(OS_CHROMEOS) case XKB_KEY_Super_L: case XKB_KEY_Super_R: - return DomKey::OS; + return DomKey::META; case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: return DomKey::HYPER; @@ -233,11 +233,11 @@ DomKey NonPrintableXKeySymToDomKey(xkb_keysym_t keysym) { case XKB_KEY_XF86Suspend: return DomKey::STANDBY; case XKB_KEY_XF86AudioLowerVolume: - return DomKey::VOLUME_DOWN; + return DomKey::AUDIO_VOLUME_DOWN; case XKB_KEY_XF86AudioMute: - return DomKey::VOLUME_MUTE; + return DomKey::AUDIO_VOLUME_MUTE; case XKB_KEY_XF86AudioRaiseVolume: - return DomKey::VOLUME_UP; + return DomKey::AUDIO_VOLUME_UP; case XKB_KEY_XF86AudioPlay: return DomKey::MEDIA_PLAY; case XKB_KEY_XF86AudioStop: diff --git a/chromium/ui/events/keycodes/platform_key_map_win.cc b/chromium/ui/events/keycodes/platform_key_map_win.cc new file mode 100644 index 00000000000..e6760c8944f --- /dev/null +++ b/chromium/ui/events/keycodes/platform_key_map_win.cc @@ -0,0 +1,281 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/keycodes/platform_key_map_win.h" + +#include <utility> + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/threading/thread_local_storage.h" + +#include "ui/events/event_constants.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" + +namespace ui { + +namespace { + +struct DomCodeEntry { + DomCode dom_code; + int scan_code; +}; +#define USB_KEYMAP_DECLARATION const DomCodeEntry supported_dom_code_list[] = +#define USB_KEYMAP(usb, evdev, xkb, win, mac, code, id) {DomCode::id, win} +#include "ui/events/keycodes/dom/keycode_converter_data.inc" +#undef USB_KEYMAP +#undef USB_KEYMAP_DECLARATION + +// List of modifiers mentioned in https://w3c.github.io/uievents/#keys-modifiers +// Some modifiers are commented out because they usually don't change keys. +const EventFlags modifier_flags[] = { + EF_SHIFT_DOWN, + EF_CONTROL_DOWN, + EF_ALT_DOWN, + // EF_COMMAND_DOWN, + EF_ALTGR_DOWN, + // EF_NUM_LOCK_ON, + EF_CAPS_LOCK_ON, + // EF_SCROLL_LOCK_ON +}; +const int kModifierFlagsCombinations = (1 << arraysize(modifier_flags)) - 1; + +int GetModifierFlags(int combination) { + int flags = EF_NONE; + for (size_t i = 0; i < arraysize(modifier_flags); ++i) { + if (combination & (1 << i)) + flags |= modifier_flags[i]; + } + return flags; +} + +void SetModifierState(BYTE* keyboard_state, int flags) { + // According to MSDN GetKeyState(): + // 1. If the high-order bit is 1, the key is down; otherwise, it is up. + // 2. If the low-order bit is 1, the key is toggled. A key, such as the + // CAPS LOCK key, is toggled if it is turned on. The key is off and + // untoggled if the low-order bit is 0. + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301.aspx + if (flags & EF_SHIFT_DOWN) + keyboard_state[VK_SHIFT] |= 0x80; + + if (flags & EF_CONTROL_DOWN) + keyboard_state[VK_CONTROL] |= 0x80; + + if (flags & EF_ALT_DOWN) + keyboard_state[VK_MENU] |= 0x80; + + if (flags & EF_ALTGR_DOWN) { + // AltGr should be RightAlt+LeftControl within Windows, but actually only + // the non-located keys will work here. + keyboard_state[VK_MENU] |= 0x80; + keyboard_state[VK_CONTROL] |= 0x80; + } + + if (flags & EF_COMMAND_DOWN) + keyboard_state[VK_LWIN] |= 0x80; + + if (flags & EF_NUM_LOCK_ON) + keyboard_state[VK_NUMLOCK] |= 0x01; + + if (flags & EF_CAPS_LOCK_ON) + keyboard_state[VK_CAPITAL] |= 0x01; + + if (flags & EF_SCROLL_LOCK_ON) + keyboard_state[VK_SCROLL] |= 0x01; +} + +DomKey NumPadKeyCodeToDomKey(KeyboardCode key_code) { + switch (key_code) { + case VKEY_NUMPAD0: + return DomKey::Constant<'0'>::Character; + case VKEY_NUMPAD1: + return DomKey::Constant<'1'>::Character; + case VKEY_NUMPAD2: + return DomKey::Constant<'2'>::Character; + case VKEY_NUMPAD3: + return DomKey::Constant<'3'>::Character; + case VKEY_NUMPAD4: + return DomKey::Constant<'4'>::Character; + case VKEY_NUMPAD5: + return DomKey::Constant<'5'>::Character; + case VKEY_NUMPAD6: + return DomKey::Constant<'6'>::Character; + case VKEY_NUMPAD7: + return DomKey::Constant<'7'>::Character; + case VKEY_NUMPAD8: + return DomKey::Constant<'8'>::Character; + case VKEY_NUMPAD9: + return DomKey::Constant<'9'>::Character; + case VKEY_CLEAR: + return DomKey::CLEAR; + case VKEY_PRIOR: + return DomKey::PAGE_UP; + case VKEY_NEXT: + return DomKey::PAGE_DOWN; + case VKEY_END: + return DomKey::END; + case VKEY_HOME: + return DomKey::HOME; + case VKEY_LEFT: + return DomKey::ARROW_LEFT; + case VKEY_UP: + return DomKey::ARROW_UP; + case VKEY_RIGHT: + return DomKey::ARROW_RIGHT; + case VKEY_DOWN: + return DomKey::ARROW_DOWN; + case VKEY_INSERT: + return DomKey::INSERT; + case VKEY_DELETE: + return DomKey::DEL; + default: + return DomKey::NONE; + } +} + +void CleanupKeyMapTls(void* data) { + PlatformKeyMap* key_map = reinterpret_cast<PlatformKeyMap*>(data); + delete key_map; +} + +struct PlatformKeyMapInstanceTlsTraits + : public base::DefaultLazyInstanceTraits<base::ThreadLocalStorage::Slot> { + static base::ThreadLocalStorage::Slot* New(void* instance) { + // Use placement new to initialize our instance in our preallocated space. + // TODO(chongz): Use std::default_delete instead of providing own function. + return new (instance) base::ThreadLocalStorage::Slot(CleanupKeyMapTls); + } +}; + +base::LazyInstance<base::ThreadLocalStorage::Slot, + PlatformKeyMapInstanceTlsTraits> + g_platform_key_map_tls_lazy = LAZY_INSTANCE_INITIALIZER; + +} // anonymous namespace + +PlatformKeyMap::PlatformKeyMap() {} + +PlatformKeyMap::PlatformKeyMap(HKL layout) { + UpdateLayout(layout); +} + +PlatformKeyMap::~PlatformKeyMap() {} + +DomKey PlatformKeyMap::DomKeyFromNativeImpl(DomCode code, + KeyboardCode key_code, + int flags) const { + if (KeycodeConverter::DomCodeToLocation(code) == DomKeyLocation::NUMPAD) { + // Derived the DOM Key value from |key_code| instead of |code|, to address + // Windows Numlock/Shift interaction - see crbug.com/594552. + return NumPadKeyCodeToDomKey(key_code); + } + + const int flags_to_try[] = { + // Trying to match Firefox's behavior and UIEvents DomKey guidelines. + // If the combination doesn't produce a printable character, the key value + // should be the key with no modifiers except for Shift and AltGr. + // See https://w3c.github.io/uievents/#keys-guidelines + flags, + flags & (EF_SHIFT_DOWN | EF_ALTGR_DOWN | EF_CAPS_LOCK_ON), + flags & (EF_SHIFT_DOWN | EF_CAPS_LOCK_ON), + EF_NONE, + }; + + DomKey key = DomKey::NONE; + for (auto try_flags : flags_to_try) { + const auto& it = code_to_key_.find(std::make_pair(static_cast<int>(code), + try_flags)); + if (it != code_to_key_.end()) { + key = it->second; + if (key != DomKey::NONE) + break; + } + } + return key; +} + +// static +DomKey PlatformKeyMap::DomKeyFromNative(const base::NativeEvent& native_event) { + // Use TLS because KeyboardLayout is per thread. + // However currently PlatformKeyMap will only be used by the host application, + // which is just one process and one thread. + base::ThreadLocalStorage::Slot* platform_key_map_tls = + g_platform_key_map_tls_lazy.Pointer(); + PlatformKeyMap* platform_key_map = + reinterpret_cast<PlatformKeyMap*>(platform_key_map_tls->Get()); + if (!platform_key_map) { + platform_key_map = new PlatformKeyMap(); + platform_key_map_tls->Set(platform_key_map); + } + + HKL current_layout = ::GetKeyboardLayout(0); + platform_key_map->UpdateLayout(current_layout); + return platform_key_map->DomKeyFromNativeImpl( + CodeFromNative(native_event), KeyboardCodeFromNative(native_event), + EventFlagsFromNative(native_event)); +} + +void PlatformKeyMap::UpdateLayout(HKL layout) { + if (layout == keyboard_layout_) + return; + + BYTE keyboard_state_to_restore[256]; + if (!::GetKeyboardState(keyboard_state_to_restore)) + return; + + // TODO(chongz): Optimize layout switching (see crbug.com/587147). + keyboard_layout_ = layout; + code_to_key_.clear(); + // Map size for some sample keyboard layouts: + // US: 428, French: 554, Persian: 434, Vietnamese: 1388 + code_to_key_.reserve(500); + + for (int eindex = 0; eindex <= kModifierFlagsCombinations; ++eindex) { + BYTE keyboard_state[256]; + memset(keyboard_state, 0, sizeof(keyboard_state)); + int flags = GetModifierFlags(eindex); + SetModifierState(keyboard_state, flags); + for (const auto& dom_code_entry : supported_dom_code_list) { + wchar_t translated_chars[5]; + int key_code = ::MapVirtualKeyEx(dom_code_entry.scan_code, + MAPVK_VSC_TO_VK, keyboard_layout_); + int rv = ::ToUnicodeEx(key_code, 0, keyboard_state, translated_chars, + arraysize(translated_chars), 0, keyboard_layout_); + + if (rv == -1) { + // Dead key, injecting VK_SPACE to get character representation. + BYTE empty_state[256]; + memset(empty_state, 0, sizeof(empty_state)); + rv = ::ToUnicodeEx(VK_SPACE, 0, empty_state, translated_chars, + arraysize(translated_chars), 0, keyboard_layout_); + // Expecting a dead key character (not followed by a space). + if (rv == 1) { + code_to_key_[std::make_pair(static_cast<int>(dom_code_entry.dom_code), + flags)] = + DomKey::DeadKeyFromCombiningCharacter(translated_chars[0]); + } else { + // TODO(chongz): Check if this will actually happen. + } + } else if (rv == 1) { + if (translated_chars[0] >= 0x20) { + code_to_key_[std::make_pair(static_cast<int>(dom_code_entry.dom_code), + flags)] = + DomKey::FromCharacter(translated_chars[0]); + } else { + // Ignores legacy non-printable control characters. + } + } else { + // TODO(chongz): Handle rv <= -2 and rv >= 2. + } + } + } + ::SetKeyboardState(keyboard_state_to_restore); +} + +} // namespace ui diff --git a/chromium/ui/events/keycodes/platform_key_map_win.h b/chromium/ui/events/keycodes/platform_key_map_win.h new file mode 100644 index 00000000000..6d128a7a5dc --- /dev/null +++ b/chromium/ui/events/keycodes/platform_key_map_win.h @@ -0,0 +1,65 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_KEYCODES_PLATFORM_KEY_MAP_WIN_H_ +#define UI_EVENTS_KEYCODES_PLATFORM_KEY_MAP_WIN_H_ + +#include <windows.h> + +#include <unordered_map> + +#include "base/event_types.h" +#include "base/hash.h" +#include "ui/events/events_export.h" +#include "ui/events/keycodes/dom/dom_key.h" +#include "ui/events/keycodes/keyboard_codes_win.h" + +namespace ui { + +enum class DomCode; + +class EVENTS_EXPORT PlatformKeyMap { + public: + // Create and load key map table with specified keyboard layout. + // Visible for testing. + explicit PlatformKeyMap(HKL layout); + ~PlatformKeyMap(); + + // Returns the DOM KeyboardEvent key from a native event and the keyboard + // layout of current thread. + // Updates a per-thread key map cache whenever the layout changes. + static DomKey DomKeyFromNative(const base::NativeEvent& native_event); + + private: + friend class PlatformKeyMapTest; + + PlatformKeyMap(); + + // TODO(chongz): Expose this function when we need to access separate layout. + // Returns the DomKey 'meaning' of |code| in the context of specified + // |ui_event_flags| and stored keyboard layout. + // |key_code| will only be used for NumPad. + DomKey DomKeyFromNativeImpl(DomCode code, + KeyboardCode key_code, + int ui_event_flags) const; + + // TODO(chongz): Expose this function in response to WM_INPUTLANGCHANGE. + void UpdateLayout(HKL layout); + + HKL keyboard_layout_ = 0; + + // TODO(chongz): Change type to DomCode when we got a generic pair hash class. + typedef std::pair<int /*DomCode*/, int /*EventFlags*/> DomCodeEventFlagsPair; + typedef std::unordered_map<DomCodeEventFlagsPair, + DomKey, + base::IntPairHash<std::pair<int, int>>> + DomCodeToKeyMap; + DomCodeToKeyMap code_to_key_; + + DISALLOW_COPY_AND_ASSIGN(PlatformKeyMap); +}; + +} // namespace ui + +#endif // UI_EVENTS_KEYCODES_PLATFORM_KEY_MAP_WIN_H_ diff --git a/chromium/ui/events/keycodes/platform_key_map_win_unittest.cc b/chromium/ui/events/keycodes/platform_key_map_win_unittest.cc new file mode 100644 index 00000000000..44cbd586715 --- /dev/null +++ b/chromium/ui/events/keycodes/platform_key_map_win_unittest.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/keycodes/platform_key_map_win.h" + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/keycodes/dom/dom_key.h" +#include "ui/events/keycodes/dom/keycode_converter.h" + +namespace ui { + +namespace { + +const wchar_t* LAYOUT_US = L"00000409"; +const wchar_t* LAYOUT_FR = L"0000040c"; + +struct TestKey { + // Have to use KeyboardCode instead of DomCode because we don't know the + // physical keyboard layout for try bots. + KeyboardCode key_code; + const char* normal; + const char* shift; + const char* capslock; + const char* altgr; + const char* shift_capslock; + const char* shift_altgr; + const char* altgr_capslock; +}; + +} // anonymous namespace + +class PlatformKeyMapTest : public testing::Test { + public: + PlatformKeyMapTest() {} + ~PlatformKeyMapTest() override {} + + void CheckDomCodeToKeyString(const char* label, + const PlatformKeyMap& keymap, + const TestKey& test_case, + HKL layout) { + KeyboardCode key_code = test_case.key_code; + int scan_code = ::MapVirtualKeyEx(key_code, MAPVK_VK_TO_VSC, layout); + DomCode dom_code = KeycodeConverter::NativeKeycodeToDomCode(scan_code); + EXPECT_STREQ(test_case.normal, + KeycodeConverter::DomKeyToKeyString( + keymap.DomKeyFromNativeImpl(dom_code, key_code, EF_NONE)) + .c_str()) + << label; + EXPECT_STREQ(test_case.shift, KeycodeConverter::DomKeyToKeyString( + keymap.DomKeyFromNativeImpl( + dom_code, key_code, EF_SHIFT_DOWN)) + .c_str()) + << label; + EXPECT_STREQ( + test_case.capslock, + KeycodeConverter::DomKeyToKeyString( + keymap.DomKeyFromNativeImpl(dom_code, key_code, EF_CAPS_LOCK_ON)) + .c_str()) + << label; + EXPECT_STREQ(test_case.altgr, KeycodeConverter::DomKeyToKeyString( + keymap.DomKeyFromNativeImpl( + dom_code, key_code, EF_ALTGR_DOWN)) + .c_str()) + << label; + EXPECT_STREQ(test_case.shift_capslock, + KeycodeConverter::DomKeyToKeyString( + keymap.DomKeyFromNativeImpl( + dom_code, key_code, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON)) + .c_str()) + << label; + EXPECT_STREQ(test_case.shift_altgr, + KeycodeConverter::DomKeyToKeyString( + keymap.DomKeyFromNativeImpl(dom_code, key_code, + EF_SHIFT_DOWN | EF_ALTGR_DOWN)) + .c_str()) + << label; + EXPECT_STREQ(test_case.altgr_capslock, + KeycodeConverter::DomKeyToKeyString( + keymap.DomKeyFromNativeImpl( + dom_code, key_code, EF_ALTGR_DOWN | EF_CAPS_LOCK_ON)) + .c_str()) + << label; + } + + DomKey DomKeyFromNativeImpl(const PlatformKeyMap& keymap, + DomCode dom_code, + KeyboardCode key_code, + int flags) { + return keymap.DomKeyFromNativeImpl(dom_code, key_code, flags); + } + + private: + DISALLOW_COPY_AND_ASSIGN(PlatformKeyMapTest); +}; + +TEST_F(PlatformKeyMapTest, USLayout) { + HKL layout = ::LoadKeyboardLayout(LAYOUT_US, 0); + PlatformKeyMap keymap(layout); + + const TestKey kUSLayoutTestCases[] = { + // n s c a sc sa ac + {VKEY_0, "0", ")", "0", "0", ")", ")", "0"}, + {VKEY_1, "1", "!", "1", "1", "!", "!", "1"}, + {VKEY_2, "2", "@", "2", "2", "@", "@", "2"}, + {VKEY_3, "3", "#", "3", "3", "#", "#", "3"}, + {VKEY_4, "4", "$", "4", "4", "$", "$", "4"}, + {VKEY_5, "5", "%", "5", "5", "%", "%", "5"}, + {VKEY_6, "6", "^", "6", "6", "^", "^", "6"}, + {VKEY_7, "7", "&", "7", "7", "&", "&", "7"}, + {VKEY_8, "8", "*", "8", "8", "*", "*", "8"}, + {VKEY_9, "9", "(", "9", "9", "(", "(", "9"}, + {VKEY_A, "a", "A", "A", "a", "a", "A", "A"}, + {VKEY_B, "b", "B", "B", "b", "b", "B", "B"}, + {VKEY_C, "c", "C", "C", "c", "c", "C", "C"}, + {VKEY_D, "d", "D", "D", "d", "d", "D", "D"}, + {VKEY_E, "e", "E", "E", "e", "e", "E", "E"}, + {VKEY_F, "f", "F", "F", "f", "f", "F", "F"}, + {VKEY_G, "g", "G", "G", "g", "g", "G", "G"}, + {VKEY_H, "h", "H", "H", "h", "h", "H", "H"}, + {VKEY_I, "i", "I", "I", "i", "i", "I", "I"}, + {VKEY_J, "j", "J", "J", "j", "j", "J", "J"}, + {VKEY_K, "k", "K", "K", "k", "k", "K", "K"}, + {VKEY_L, "l", "L", "L", "l", "l", "L", "L"}, + {VKEY_M, "m", "M", "M", "m", "m", "M", "M"}, + {VKEY_N, "n", "N", "N", "n", "n", "N", "N"}, + {VKEY_O, "o", "O", "O", "o", "o", "O", "O"}, + {VKEY_P, "p", "P", "P", "p", "p", "P", "P"}, + {VKEY_Q, "q", "Q", "Q", "q", "q", "Q", "Q"}, + {VKEY_R, "r", "R", "R", "r", "r", "R", "R"}, + {VKEY_S, "s", "S", "S", "s", "s", "S", "S"}, + {VKEY_T, "t", "T", "T", "t", "t", "T", "T"}, + {VKEY_U, "u", "U", "U", "u", "u", "U", "U"}, + {VKEY_V, "v", "V", "V", "v", "v", "V", "V"}, + {VKEY_W, "w", "W", "W", "w", "w", "W", "W"}, + {VKEY_X, "x", "X", "X", "x", "x", "X", "X"}, + {VKEY_Y, "y", "Y", "Y", "y", "y", "Y", "Y"}, + {VKEY_Z, "z", "Z", "Z", "z", "z", "Z", "Z"}, + }; + + for (const auto& test_case : kUSLayoutTestCases) { + CheckDomCodeToKeyString("USLayout", keymap, test_case, layout); + } +} + +TEST_F(PlatformKeyMapTest, FRLayout) { + HKL layout = ::LoadKeyboardLayout(LAYOUT_FR, 0); + PlatformKeyMap keymap(layout); + + const TestKey kFRLayoutTestCases[] = { + // n s c a sc sa ac + {VKEY_0, "à", "0", "0", "@", "à", "0", "@"}, + {VKEY_1, "&", "1", "1", "&", "&", "1", "1"}, + {VKEY_2, "é", "2", "2", "Dead", "é", "2", "Dead"}, + {VKEY_3, "\"", "3", "3", "#", "\"", "3", "#"}, + {VKEY_4, "\'", "4", "4", "{", "\'", "4", "{"}, + {VKEY_5, "(", "5", "5", "[", "(", "5", "["}, + {VKEY_6, "-", "6", "6", "|", "-", "6", "|"}, + {VKEY_7, "è", "7", "7", "Dead", "è", "7", "Dead"}, + {VKEY_8, "_", "8", "8", "\\", "_", "8", "\\"}, + {VKEY_9, "ç", "9", "9", "^", "ç", "9", "^"}, + {VKEY_A, "a", "A", "A", "a", "a", "A", "A"}, + {VKEY_B, "b", "B", "B", "b", "b", "B", "B"}, + {VKEY_C, "c", "C", "C", "c", "c", "C", "C"}, + {VKEY_D, "d", "D", "D", "d", "d", "D", "D"}, + {VKEY_E, "e", "E", "E", "€", "e", "E", "€"}, + {VKEY_F, "f", "F", "F", "f", "f", "F", "F"}, + {VKEY_G, "g", "G", "G", "g", "g", "G", "G"}, + {VKEY_H, "h", "H", "H", "h", "h", "H", "H"}, + {VKEY_I, "i", "I", "I", "i", "i", "I", "I"}, + {VKEY_J, "j", "J", "J", "j", "j", "J", "J"}, + {VKEY_K, "k", "K", "K", "k", "k", "K", "K"}, + {VKEY_L, "l", "L", "L", "l", "l", "L", "L"}, + {VKEY_M, "m", "M", "M", "m", "m", "M", "M"}, + {VKEY_N, "n", "N", "N", "n", "n", "N", "N"}, + {VKEY_O, "o", "O", "O", "o", "o", "O", "O"}, + {VKEY_P, "p", "P", "P", "p", "p", "P", "P"}, + {VKEY_Q, "q", "Q", "Q", "q", "q", "Q", "Q"}, + {VKEY_R, "r", "R", "R", "r", "r", "R", "R"}, + {VKEY_S, "s", "S", "S", "s", "s", "S", "S"}, + {VKEY_T, "t", "T", "T", "t", "t", "T", "T"}, + {VKEY_U, "u", "U", "U", "u", "u", "U", "U"}, + {VKEY_V, "v", "V", "V", "v", "v", "V", "V"}, + {VKEY_W, "w", "W", "W", "w", "w", "W", "W"}, + {VKEY_X, "x", "X", "X", "x", "x", "X", "X"}, + {VKEY_Y, "y", "Y", "Y", "y", "y", "Y", "Y"}, + {VKEY_Z, "z", "Z", "Z", "z", "z", "Z", "Z"}, + }; + + for (const auto& test_case : kFRLayoutTestCases) { + CheckDomCodeToKeyString("FRLayout", keymap, test_case, layout); + } +} + +TEST_F(PlatformKeyMapTest, NumPad) { + HKL layout = ::LoadKeyboardLayout(LAYOUT_US, 0); + PlatformKeyMap keymap(layout); + + const struct TestCase { + KeyboardCode key_code; + DomKey key; + } kNumPadTestCases[] = { + {VKEY_NUMPAD0, DomKey::FromCharacter('0')}, + {VKEY_NUMPAD1, DomKey::FromCharacter('1')}, + {VKEY_NUMPAD2, DomKey::FromCharacter('2')}, + {VKEY_NUMPAD3, DomKey::FromCharacter('3')}, + {VKEY_NUMPAD4, DomKey::FromCharacter('4')}, + {VKEY_NUMPAD5, DomKey::FromCharacter('5')}, + {VKEY_NUMPAD6, DomKey::FromCharacter('6')}, + {VKEY_NUMPAD7, DomKey::FromCharacter('7')}, + {VKEY_NUMPAD8, DomKey::FromCharacter('8')}, + {VKEY_NUMPAD9, DomKey::FromCharacter('9')}, + {VKEY_CLEAR, DomKey::CLEAR}, + {VKEY_PRIOR, DomKey::PAGE_UP}, + {VKEY_NEXT, DomKey::PAGE_DOWN}, + {VKEY_END, DomKey::END}, + {VKEY_HOME, DomKey::HOME}, + {VKEY_LEFT, DomKey::ARROW_LEFT}, + {VKEY_UP, DomKey::ARROW_UP}, + {VKEY_RIGHT, DomKey::ARROW_RIGHT}, + {VKEY_DOWN, DomKey::ARROW_DOWN}, + {VKEY_INSERT, DomKey::INSERT}, + {VKEY_DELETE, DomKey::DEL}, + }; + + for (const auto& test_case : kNumPadTestCases) { + KeyboardCode key_code = test_case.key_code; + int scan_code = ::MapVirtualKeyEx(key_code, MAPVK_VK_TO_VSC, layout); + DomCode dom_code = KeycodeConverter::NativeKeycodeToDomCode(scan_code); + + EXPECT_EQ(test_case.key, + DomKeyFromNativeImpl(keymap, dom_code, key_code, EF_NONE)) + << key_code; + EXPECT_EQ(test_case.key, + DomKeyFromNativeImpl(keymap, dom_code, key_code, EF_ALTGR_DOWN)) + << key_code; + EXPECT_EQ(test_case.key, + DomKeyFromNativeImpl(keymap, dom_code, key_code, EF_CONTROL_DOWN)) + << key_code; + EXPECT_EQ(test_case.key, + DomKeyFromNativeImpl(keymap, dom_code, key_code, + EF_ALTGR_DOWN | EF_CONTROL_DOWN)) + << key_code; + } +} + +} // namespace ui diff --git a/chromium/ui/events/keycodes/xkb_keysym.h b/chromium/ui/events/keycodes/xkb_keysym.h index e0847d2d4bd..79d1f1f95f6 100644 --- a/chromium/ui/events/keycodes/xkb_keysym.h +++ b/chromium/ui/events/keycodes/xkb_keysym.h @@ -2,13 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_EVENTS_KEYCODES_XKEYSYMS_H_ -#define UI_EVENTS_KEYCODES_XKEYSYMS_H_ +#ifndef UI_EVENTS_KEYCODES_XKB_KEYSYM_H_ +#define UI_EVENTS_KEYCODES_XKB_KEYSYM_H_ // This file provides definitions of the xkbcommon keysym type (xkb_keysym_t) // and values (XKB_KEY_...) for both xkbcommon and traditional X11. -#if defined(USE_X11) +#if defined(USE_XKBCOMMON) + +#include <xkbcommon/xkbcommon.h> +#include <xkbcommon/xkbcommon-keysyms.h> + +#else // !defined(USE_XKBCOMMON) #define XK_3270 // For XK_3270_BackTab in particular. #include <X11/X.h> @@ -16,7 +21,7 @@ #include <X11/Sunkeysym.h> #include <X11/XF86keysym.h> -using xkb_keysym_t = KeySym; +using xkb_keysym_t = uint32_t; #define XKB_KEY_3270_Duplicate XK_3270_Duplicate #define XKB_KEY_3270_FieldMark XK_3270_FieldMark @@ -525,10 +530,7 @@ using xkb_keysym_t = KeySym; #define XKB_KEY_XF86TouchpadOff XF86XK_TouchpadOff #define XKB_KEY_XF86AudioMicMute XF86XK_AudioMicMute -#else // not defined(USE_X11) -#include <xkbcommon/xkbcommon.h> -#include <xkbcommon/xkbcommon-keysyms.h> #endif -#endif // UI_EVENTS_KEYCODES_XKEYSYMS_H_ +#endif // UI_EVENTS_KEYCODES_XKB_KEYSYM_H_ diff --git a/chromium/ui/events/latency_info.cc b/chromium/ui/events/latency_info.cc index cd03d4c9091..d935564a7d0 100644 --- a/chromium/ui/events/latency_info.cc +++ b/chromium/ui/events/latency_info.cc @@ -37,6 +37,7 @@ const char* GetComponentName(ui::LatencyComponentType type) { CASE_TYPE(INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT); CASE_TYPE(INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT); CASE_TYPE(INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT); + CASE_TYPE(INPUT_EVENT_LATENCY_GENERATE_SCROLL_UPDATE_FROM_MOUSE_WHEEL); CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT); CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_MOUSE_WHEEL_COMPONENT); CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_KEYBOARD_COMPONENT); @@ -84,7 +85,7 @@ bool IsInputLatencyBeginComponent(ui::LatencyComponentType type) { class LatencyInfoTracedValue : public base::trace_event::ConvertableToTraceFormat { public: - static scoped_refptr<ConvertableToTraceFormat> FromValue( + static scoped_ptr<ConvertableToTraceFormat> FromValue( scoped_ptr<base::Value> value); void AppendAsTraceFormat(std::string* out) const override; @@ -98,9 +99,9 @@ class LatencyInfoTracedValue DISALLOW_COPY_AND_ASSIGN(LatencyInfoTracedValue); }; -scoped_refptr<base::trace_event::ConvertableToTraceFormat> +scoped_ptr<base::trace_event::ConvertableToTraceFormat> LatencyInfoTracedValue::FromValue(scoped_ptr<base::Value> value) { - return scoped_refptr<base::trace_event::ConvertableToTraceFormat>( + return scoped_ptr<base::trace_event::ConvertableToTraceFormat>( new LatencyInfoTracedValue(value.release())); } @@ -143,17 +144,16 @@ LatencyInfo::InputCoordinate::InputCoordinate(float x, float y) : x(x), y(y) { LatencyInfo::LatencyInfo() : input_coordinates_size_(0), - coalesced_events_size_(0), trace_id_(-1), - terminated_(false) { -} + coalesced_(false), + terminated_(false) {} -LatencyInfo::~LatencyInfo() { -} +LatencyInfo::LatencyInfo(const LatencyInfo& other) = default; + +LatencyInfo::~LatencyInfo() {} LatencyInfo::LatencyInfo(int64_t trace_id, bool terminated) : input_coordinates_size_(0), - coalesced_events_size_(0), trace_id_(trace_id), terminated_(terminated) {} @@ -315,7 +315,7 @@ void LatencyInfo::AddLatencyNumberWithTimestampImpl( } } -scoped_refptr<base::trace_event::ConvertableToTraceFormat> +scoped_ptr<base::trace_event::ConvertableToTraceFormat> LatencyInfo::AsTraceableData() { scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue()); for (const auto& lc : latency_components_) { @@ -335,7 +335,7 @@ LatencyInfo::AsTraceableData() { return LatencyInfoTracedValue::FromValue(std::move(record_data)); } -scoped_refptr<base::trace_event::ConvertableToTraceFormat> +scoped_ptr<base::trace_event::ConvertableToTraceFormat> LatencyInfo::CoordinatesAsTraceableData() { scoped_ptr<base::ListValue> coordinates(new base::ListValue()); for (size_t i = 0; i < input_coordinates_size_; i++) { @@ -380,11 +380,4 @@ bool LatencyInfo::AddInputCoordinate(const InputCoordinate& input_coordinate) { return true; } -bool LatencyInfo::AddCoalescedEventTimestamp(double timestamp) { - if (coalesced_events_size_ >= kMaxCoalescedEventTimestamps) - return false; - timestamps_of_coalesced_events_[coalesced_events_size_++] = timestamp; - return true; -} - } // namespace ui diff --git a/chromium/ui/events/latency_info.h b/chromium/ui/events/latency_info.h index 77efe5f7be3..cf61526ce35 100644 --- a/chromium/ui/events/latency_info.h +++ b/chromium/ui/events/latency_info.h @@ -15,9 +15,12 @@ #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" -#include "ipc/ipc_param_traits.h" #include "ui/events/events_base_export.h" +#if !defined(OS_IOS) +#include "ipc/ipc_param_traits.h" // nogncheck +#endif + namespace ui { // When adding new components, or new metrics based on LatencyInfo, @@ -64,6 +67,9 @@ enum LatencyComponentType { // Timestamp of when the gpu service began swap buffers, unlike // INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT which measures after. INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, + // Timestamp of when the gesture scroll update is generated from a mouse wheel + // event. + INPUT_EVENT_LATENCY_GENERATE_SCROLL_UPDATE_FROM_MOUSE_WHEEL, // ---------------------------TERMINAL COMPONENT----------------------------- // TERMINAL COMPONENT is when we show the latency end in chrome://tracing. // Timestamp when the mouse event is acked from renderer and it does not @@ -76,10 +82,10 @@ enum LatencyComponentType { // cause any rendering scheduled. INPUT_EVENT_LATENCY_TERMINATED_KEYBOARD_COMPONENT, // Timestamp when the touch event is acked from renderer and it does not - // cause any rendering schedueld and does not generate any gesture event. + // cause any rendering scheduled and does not generate any gesture event. INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, // Timestamp when the gesture event is acked from renderer, and it does not - // cause any rendering schedueld. + // cause any rendering scheduled. INPUT_EVENT_LATENCY_TERMINATED_GESTURE_COMPONENT, // Timestamp when the frame is swapped (i.e. when the rendering caused by // input event actually takes effect). @@ -94,7 +100,7 @@ enum LatencyComponentType { // but the swap failed. INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT, LATENCY_COMPONENT_TYPE_LAST = - INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT, + INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT, }; class EVENTS_BASE_EXPORT LatencyInfo { @@ -121,7 +127,6 @@ class EVENTS_BASE_EXPORT LatencyInfo { // Empirically determined constant based on a typical scroll sequence. enum { kTypicalMaxComponentsPerLatencyInfo = 10 }; - enum { kMaxCoalescedEventTimestamps = 2 }; enum { kMaxInputCoordinates = 2 }; // Map a Latency Component (with a component-specific int64_t id) to a @@ -131,6 +136,7 @@ class EVENTS_BASE_EXPORT LatencyInfo { kTypicalMaxComponentsPerLatencyInfo> LatencyMap; LatencyInfo(); + LatencyInfo(const LatencyInfo& other); ~LatencyInfo(); // For test only. @@ -192,18 +198,11 @@ class EVENTS_BASE_EXPORT LatencyInfo { return input_coordinates_; } - // Returns true if there is still room for keeping the |timestamp|, - // false otherwise. - bool AddCoalescedEventTimestamp(double timestamp); - - uint32_t coalesced_events_size() const { return coalesced_events_size_; } - const double* timestamps_of_coalesced_events() const { - return timestamps_of_coalesced_events_; - } - const LatencyMap& latency_components() const { return latency_components_; } bool terminated() const { return terminated_; } + void set_coalesced() { coalesced_ = true; } + bool coalesced() const { return coalesced_; } int64_t trace_id() const { return trace_id_; } private: @@ -215,9 +214,9 @@ class EVENTS_BASE_EXPORT LatencyInfo { const char* trace_name_str); // Converts latencyinfo into format that can be dumped into trace buffer. - scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsTraceableData(); - scoped_refptr<base::trace_event::ConvertableToTraceFormat> - CoordinatesAsTraceableData(); + scoped_ptr<base::trace_event::ConvertableToTraceFormat> AsTraceableData(); + scoped_ptr<base::trace_event::ConvertableToTraceFormat> + CoordinatesAsTraceableData(); // Shown as part of the name of the trace event for this LatencyInfo. // String is empty if no tracing is enabled. @@ -229,15 +228,16 @@ class EVENTS_BASE_EXPORT LatencyInfo { uint32_t input_coordinates_size_; InputCoordinate input_coordinates_[kMaxInputCoordinates]; - uint32_t coalesced_events_size_; - double timestamps_of_coalesced_events_[kMaxCoalescedEventTimestamps]; - // The unique id for matching the ASYNC_BEGIN/END trace event. int64_t trace_id_; + // Whether this event has been coalesced into another event. + bool coalesced_; // Whether a terminal component has been added. bool terminated_; +#if !defined(OS_IOS) friend struct IPC::ParamTraits<ui::LatencyInfo>; +#endif }; } // namespace ui diff --git a/chromium/ui/events/latency_info_unittest.cc b/chromium/ui/events/latency_info_unittest.cc index a3dac964e5f..e27986397ea 100644 --- a/chromium/ui/events/latency_info_unittest.cc +++ b/chromium/ui/events/latency_info_unittest.cc @@ -67,16 +67,4 @@ TEST(LatencyInfoTest, AddTwoSameEvent) { EXPECT_EQ(component.event_time.ToInternalValue(), (100 * 2 + 200 * 3) / 5); } -TEST(LatencyInfoTest, AddCoalescedEventTimestamp) { - LatencyInfo info; - ASSERT_EQ(0u, info.coalesced_events_size()); - for (size_t i = 0; i < LatencyInfo::kMaxCoalescedEventTimestamps; i++) - EXPECT_TRUE(info.AddCoalescedEventTimestamp(i * 10.0)); - EXPECT_FALSE(info.AddCoalescedEventTimestamp(99.0)); - EXPECT_EQ(LatencyInfo::kMaxCoalescedEventTimestamps, - info.coalesced_events_size()); - for (size_t i = 0; i < info.coalesced_events_size(); i++) - EXPECT_EQ(i * 10.0, info.timestamps_of_coalesced_events()[i]); -} - } // namespace ui diff --git a/chromium/ui/events/null_event_targeter.h b/chromium/ui/events/null_event_targeter.h index c22e7a7681f..8d9ea4312ae 100644 --- a/chromium/ui/events/null_event_targeter.h +++ b/chromium/ui/events/null_event_targeter.h @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifndef UI_EVENTS_NULL_EVENT_TARGETER_H_ +#define UI_EVENTS_NULL_EVENT_TARGETER_H_ + #include "base/compiler_specific.h" #include "base/macros.h" #include "ui/events/event_targeter.h" @@ -26,3 +29,5 @@ class EVENTS_EXPORT NullEventTargeter : public EventTargeter { }; } // namespace ui + +#endif // UI_EVENTS_NULL_EVENT_TARGETER_H_ diff --git a/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc b/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc index ca9f0d363e7..16afecc4a93 100644 --- a/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc +++ b/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc @@ -116,17 +116,14 @@ TouchEventParams::TouchEventParams(int device_id, int slot, EventType type, const gfx::PointF& location, - const gfx::Vector2dF& radii, - float pressure, + const PointerDetails& details, const base::TimeDelta& timestamp) : device_id(device_id), slot(slot), type(type), location(location), - radii(radii), - pressure(pressure), - timestamp(timestamp) { -} + pointer_details(details), + timestamp(timestamp) {} TouchEventParams::TouchEventParams(const TouchEventParams& other) = default; diff --git a/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h b/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h index 6c4c4b602b9..164d9f70351 100644 --- a/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h +++ b/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h @@ -125,8 +125,7 @@ struct EVENTS_OZONE_EVDEV_EXPORT TouchEventParams { int slot, EventType type, const gfx::PointF& location, - const gfx::Vector2dF& radii, - float pressure, + const PointerDetails& pointer_details, const base::TimeDelta& timestamp); TouchEventParams(const TouchEventParams& other); ~TouchEventParams(); @@ -135,8 +134,7 @@ struct EVENTS_OZONE_EVDEV_EXPORT TouchEventParams { int slot; EventType type; gfx::PointF location; - gfx::Vector2dF radii; - float pressure; + PointerDetails pointer_details; base::TimeDelta timestamp; }; diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc index c3c8a38bd2d..3ff26c0343c 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc @@ -124,13 +124,13 @@ class EventConverterEvdevImplTest : public testing::Test { DCHECK_GT(dispatched_events_.size(), index); ui::Event* ev = dispatched_events_[index].get(); DCHECK(ev->IsKeyEvent()); - return static_cast<ui::KeyEvent*>(ev); + return ev->AsKeyEvent(); } ui::MouseEvent* dispatched_mouse_event(unsigned index) { DCHECK_GT(dispatched_events_.size(), index); ui::Event* ev = dispatched_events_[index].get(); DCHECK(ev->IsMouseEvent()); - return static_cast<ui::MouseEvent*>(ev); + return ev->AsMouseEvent(); } void ClearDispatchedEvents() { diff --git a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc index 18118290d65..c8e99ee0148 100644 --- a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc @@ -276,8 +276,8 @@ void EventFactoryEvdev::DispatchTouchEvent(const TouchEventParams& params) { float x = params.location.x(); float y = params.location.y(); - double radius_x = params.radii.x(); - double radius_y = params.radii.y(); + double radius_x = params.pointer_details.radius_x; + double radius_y = params.pointer_details.radius_y; // Transform the event to align touches to the image based on display mode. DeviceDataManager::GetInstance()->ApplyTouchTransformer(params.device_id, &x, @@ -287,16 +287,21 @@ void EventFactoryEvdev::DispatchTouchEvent(const TouchEventParams& params) { DeviceDataManager::GetInstance()->ApplyTouchRadiusScale(params.device_id, &radius_y); + PointerDetails details = params.pointer_details; + details.radius_x = radius_x; + details.radius_y = radius_y; + // params.slot is guaranteed to be < kNumTouchEvdevSlots. int touch_id = touch_id_generator_.GetGeneratedID( params.device_id * kNumTouchEvdevSlots + params.slot); - TouchEvent touch_event(params.type, gfx::Point(), - modifiers_.GetModifierFlags(), touch_id, - params.timestamp, radius_x, radius_y, - /* angle */ 0.f, params.pressure); + TouchEvent touch_event( + params.type, gfx::Point(), modifiers_.GetModifierFlags(), touch_id, + params.timestamp, /* radius_x */ 0.f, /* radius_y */ 0.f, + /* angle */ 0.f, /* force */ 0.f); touch_event.set_location_f(gfx::PointF(x, y)); touch_event.set_root_location_f(gfx::PointF(x, y)); touch_event.set_source_device_id(params.device_id); + touch_event.set_pointer_details(details); DispatchUiEvent(&touch_event); if (params.type == ET_TOUCH_RELEASED || params.type == ET_TOUCH_CANCELLED) { diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h index 8e95df65410..5469651ba24 100644 --- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h @@ -14,6 +14,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/task_runner.h" #include "ui/events/ozone/evdev/event_converter_evdev.h" #include "ui/events/ozone/evdev/event_device_info.h" diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h index 982d4d8d2d0..7c084ccc301 100644 --- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h @@ -12,6 +12,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "ui/events/ozone/evdev/events_ozone_evdev_export.h" diff --git a/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h b/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h index 02d0888fdc7..b4fc0e4bda1 100644 --- a/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h +++ b/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h @@ -9,6 +9,7 @@ #include "base/files/file_path.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "ui/events/ozone/evdev/event_converter_evdev.h" #include "ui/events/ozone/evdev/event_device_info.h" diff --git a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h index 01034e53cc3..c4bee69c0ab 100644 --- a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h +++ b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h @@ -17,6 +17,7 @@ #include "base/containers/scoped_ptr_hash_map.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "ui/events/ozone/evdev/event_device_info.h" #include "ui/events/ozone/evdev/events_ozone_evdev_export.h" diff --git a/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc index b7def520c12..bca5eda9709 100644 --- a/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc +++ b/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc @@ -209,7 +209,7 @@ class TabletEventConverterEvdevTest : public testing::Test { DCHECK_GT(dispatched_events_.size(), index); ui::Event* ev = dispatched_events_[index].get(); DCHECK(ev->IsMouseEvent()); - return static_cast<ui::MouseEvent*>(ev); + return ev->AsMouseEvent(); } void DispatchEventForTest(ui::Event* event) { @@ -418,20 +418,20 @@ TEST_F(TabletEventConverterEvdevTest, Tap) { ui::MouseEvent* event = dispatched_event(0); EXPECT_EQ(ui::ET_MOUSE_MOVED, event->type()); EXPECT_EQ(ui::EventPointerType::POINTER_TYPE_PEN, - event->pointer_details().pointer_type()); - EXPECT_FLOAT_EQ(5.625f, event->pointer_details().tilt_x()); - EXPECT_FLOAT_EQ(0.f, event->pointer_details().tilt_y()); + event->pointer_details().pointer_type); + EXPECT_FLOAT_EQ(5.625f, event->pointer_details().tilt_x); + EXPECT_FLOAT_EQ(0.f, event->pointer_details().tilt_y); event = dispatched_event(1); EXPECT_EQ(ui::ET_MOUSE_PRESSED, event->type()); EXPECT_EQ(ui::EventPointerType::POINTER_TYPE_PEN, - event->pointer_details().pointer_type()); - EXPECT_FLOAT_EQ((float)992 / 2047, event->pointer_details().force()); + event->pointer_details().pointer_type); + EXPECT_FLOAT_EQ((float)992 / 2047, event->pointer_details().force); EXPECT_EQ(true, event->IsLeftMouseButton()); event = dispatched_event(2); EXPECT_EQ(ui::EventPointerType::POINTER_TYPE_PEN, - event->pointer_details().pointer_type()); + event->pointer_details().pointer_type); EXPECT_EQ(ui::ET_MOUSE_RELEASED, event->type()); - EXPECT_FLOAT_EQ(0.0f, event->pointer_details().force()); + EXPECT_FLOAT_EQ(0.0f, event->pointer_details().force); EXPECT_EQ(true, event->IsLeftMouseButton()); } diff --git a/chromium/ui/events/ozone/evdev/touch_evdev_types.cc b/chromium/ui/events/ozone/evdev/touch_evdev_types.cc index 78807c18749..87d0cffedfc 100644 --- a/chromium/ui/events/ozone/evdev/touch_evdev_types.cc +++ b/chromium/ui/events/ozone/evdev/touch_evdev_types.cc @@ -9,6 +9,9 @@ namespace ui { InProgressTouchEvdev::InProgressTouchEvdev() { } +InProgressTouchEvdev::InProgressTouchEvdev(const InProgressTouchEvdev& other) = + default; + InProgressTouchEvdev::~InProgressTouchEvdev() {} } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/touch_evdev_types.h b/chromium/ui/events/ozone/evdev/touch_evdev_types.h index 88c6fcde75a..1efe0c62b41 100644 --- a/chromium/ui/events/ozone/evdev/touch_evdev_types.h +++ b/chromium/ui/events/ozone/evdev/touch_evdev_types.h @@ -19,6 +19,7 @@ const int kNumTouchEvdevSlots = 20; // Contains information about an in progress touch. struct EVENTS_OZONE_EVDEV_EXPORT InProgressTouchEvdev { InProgressTouchEvdev(); + InProgressTouchEvdev(const InProgressTouchEvdev& other); ~InProgressTouchEvdev(); // Whether there is new information for the touch. @@ -37,6 +38,7 @@ struct EVENTS_OZONE_EVDEV_EXPORT InProgressTouchEvdev { float radius_x = 0; float radius_y = 0; float pressure = 0; + int tool_code = 0; }; } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc index 9624ec2a4b6..513adf99e6d 100644 --- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc @@ -78,6 +78,13 @@ int32_t AbsCodeToMtCode(int32_t code) { } } +ui::EventPointerType GetPointerTypeFromEvent( + const ui::InProgressTouchEvdev& event) { + return (event.tool_code == BTN_TOOL_PEN) + ? ui::EventPointerType::POINTER_TYPE_PEN + : ui::EventPointerType::POINTER_TYPE_TOUCH; +} + const int kTrackingIdForUnusedSlot = -1; } // namespace @@ -185,6 +192,7 @@ void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) { events_[0].radius_x = 0; events_[0].radius_y = 0; events_[0].pressure = 0; + events_[0].tool_code = 0; } } @@ -310,6 +318,13 @@ void TouchEventConverterEvdev::ProcessKey(const input_event& input) { case BTN_TOUCH: case BTN_LEFT: break; + case BTN_TOOL_PEN: + if (input.value > 0) { + events_[current_slot_].tool_code = input.code; + } else { + events_[current_slot_].tool_code = 0; + } + break; default: NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code; } @@ -388,10 +403,13 @@ EventType TouchEventConverterEvdev::GetEventTypeForTouch( void TouchEventConverterEvdev::ReportEvent(const InProgressTouchEvdev& event, EventType event_type, const base::TimeDelta& timestamp) { - dispatcher_->DispatchTouchEvent(TouchEventParams( - input_device_.id, event.slot, event_type, gfx::PointF(event.x, event.y), - gfx::Vector2dF(event.radius_x, event.radius_y), event.pressure, - timestamp)); + PointerDetails details(GetPointerTypeFromEvent(event), event.radius_x, + event.radius_y, event.pressure, + /* tilt_x */ 0.0f, + /* tilt_y */ 0.0f); + dispatcher_->DispatchTouchEvent( + TouchEventParams(input_device_.id, event.slot, event_type, + gfx::PointF(event.x, event.y), details, timestamp)); } void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) { 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 8cdd92fc673..6bb00757d71 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 @@ -277,8 +277,10 @@ TEST_F(TouchEventConverterEvdevTest, TouchMove) { EXPECT_EQ(295, event.location.x()); EXPECT_EQ(421, event.location.y()); EXPECT_EQ(0, event.slot); - EXPECT_FLOAT_EQ(58.f, event.radii.x()); - EXPECT_FLOAT_EQ(0.13333334f, event.pressure); + EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, + event.pointer_details.pointer_type); + EXPECT_FLOAT_EQ(58.f, event.pointer_details.radius_x); + EXPECT_FLOAT_EQ(0.13333334f, event.pointer_details.force); // Move. dev->ConfigureReadMock(mock_kernel_queue_move, @@ -292,8 +294,10 @@ TEST_F(TouchEventConverterEvdevTest, TouchMove) { EXPECT_EQ(312, event.location.x()); EXPECT_EQ(432, event.location.y()); EXPECT_EQ(0, event.slot); - EXPECT_FLOAT_EQ(50.f, event.radii.x()); - EXPECT_FLOAT_EQ(0.16862745f, event.pressure); + EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, + event.pointer_details.pointer_type); + EXPECT_FLOAT_EQ(50.f, event.pointer_details.radius_x); + EXPECT_FLOAT_EQ(0.16862745f, event.pointer_details.force); // Release. dev->ConfigureReadMock(mock_kernel_queue_release, @@ -307,8 +311,10 @@ TEST_F(TouchEventConverterEvdevTest, TouchMove) { EXPECT_EQ(312, event.location.x()); EXPECT_EQ(432, event.location.y()); EXPECT_EQ(0, event.slot); - EXPECT_FLOAT_EQ(50.f, event.radii.x()); - EXPECT_FLOAT_EQ(0.16862745f, event.pressure); + EXPECT_EQ(EventPointerType::POINTER_TYPE_TOUCH, + event.pointer_details.pointer_type); + EXPECT_FLOAT_EQ(50.f, event.pointer_details.radius_x); + EXPECT_FLOAT_EQ(0.16862745f, event.pointer_details.force); } TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { @@ -357,7 +363,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { EXPECT_EQ(40, ev0.location.x()); EXPECT_EQ(51, ev0.location.y()); EXPECT_EQ(0, ev0.slot); - EXPECT_FLOAT_EQ(0.17647059f, ev0.pressure); + EXPECT_FLOAT_EQ(0.17647059f, ev0.pointer_details.force); // Press EXPECT_EQ(ui::ET_TOUCH_PRESSED, ev1.type); @@ -365,7 +371,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { EXPECT_EQ(101, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); - EXPECT_FLOAT_EQ(0.17647059f, ev1.pressure); + EXPECT_FLOAT_EQ(0.17647059f, ev1.pointer_details.force); // Stationary 0, Moves 1. struct input_event mock_kernel_queue_stationary0_move1[] = { @@ -381,7 +387,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { EXPECT_EQ(40, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); - EXPECT_FLOAT_EQ(0.17647059f, ev1.pressure); + EXPECT_FLOAT_EQ(0.17647059f, ev1.pointer_details.force); // Move 0, stationary 1. struct input_event mock_kernel_queue_move0_stationary1[] = { @@ -398,7 +404,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { EXPECT_EQ(39, ev0.location.x()); EXPECT_EQ(51, ev0.location.y()); EXPECT_EQ(0, ev0.slot); - EXPECT_FLOAT_EQ(0.17647059f, ev0.pressure); + EXPECT_FLOAT_EQ(0.17647059f, ev0.pointer_details.force); // Release 0, move 1. struct input_event mock_kernel_queue_release0_move1[] = { @@ -416,14 +422,14 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { EXPECT_EQ(39, ev0.location.x()); EXPECT_EQ(51, ev0.location.y()); EXPECT_EQ(0, ev0.slot); - EXPECT_FLOAT_EQ(0.17647059f, ev0.pressure); + EXPECT_FLOAT_EQ(0.17647059f, ev0.pointer_details.force); EXPECT_EQ(ui::ET_TOUCH_MOVED, ev1.type); EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev1.timestamp); EXPECT_EQ(38, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); - EXPECT_FLOAT_EQ(0.17647059f, ev1.pressure); + EXPECT_FLOAT_EQ(0.17647059f, ev1.pointer_details.force); // Release 1. struct input_event mock_kernel_queue_release1[] = { @@ -439,7 +445,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { EXPECT_EQ(38, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); - EXPECT_FLOAT_EQ(0.17647059f, ev1.pressure); + EXPECT_FLOAT_EQ(0.17647059f, ev1.pointer_details.force); } TEST_F(TouchEventConverterEvdevTest, Unsync) { @@ -509,9 +515,9 @@ TEST_F(TouchEventConverterEvdevTest, ShouldResumeExistingContactsOnStart) { ui::TouchEventParams ev = dispatched_event(0); EXPECT_EQ(ET_TOUCH_PRESSED, ev.type); EXPECT_EQ(0, ev.slot); - EXPECT_FLOAT_EQ(50.f, ev.radii.x()); - EXPECT_FLOAT_EQ(0.f, ev.radii.y()); - EXPECT_FLOAT_EQ(0.50196081f, ev.pressure); + EXPECT_FLOAT_EQ(50.f, ev.pointer_details.radius_x); + EXPECT_FLOAT_EQ(50.f, ev.pointer_details.radius_y); + EXPECT_FLOAT_EQ(0.50196081f, ev.pointer_details.force); } TEST_F(TouchEventConverterEvdevTest, ShouldReleaseContactsOnStop) { @@ -663,8 +669,8 @@ TEST_F(TouchEventConverterEvdevTest, ShouldUseLeftButtonIfNoTouchButton) { EXPECT_EQ(3654, event.location.x()); EXPECT_EQ(1055, event.location.y()); EXPECT_EQ(0, event.slot); - EXPECT_FLOAT_EQ(0.f, event.radii.x()); - EXPECT_FLOAT_EQ(0.f, event.pressure); + EXPECT_FLOAT_EQ(0.f, event.pointer_details.radius_x); + EXPECT_FLOAT_EQ(0.f, event.pointer_details.force); // Move. dev->ConfigureReadMock(mock_kernel_queue_move, @@ -678,8 +684,8 @@ TEST_F(TouchEventConverterEvdevTest, ShouldUseLeftButtonIfNoTouchButton) { EXPECT_EQ(3644, event.location.x()); EXPECT_EQ(1059, event.location.y()); EXPECT_EQ(0, event.slot); - EXPECT_FLOAT_EQ(0.f, event.radii.x()); - EXPECT_FLOAT_EQ(0.f, event.pressure); + EXPECT_FLOAT_EQ(0.f, event.pointer_details.radius_x); + EXPECT_FLOAT_EQ(0.f, event.pointer_details.force); // Release. dev->ConfigureReadMock(mock_kernel_queue_release, @@ -693,8 +699,8 @@ TEST_F(TouchEventConverterEvdevTest, ShouldUseLeftButtonIfNoTouchButton) { EXPECT_EQ(3644, event.location.x()); EXPECT_EQ(1059, event.location.y()); EXPECT_EQ(0, event.slot); - EXPECT_FLOAT_EQ(0.f, event.radii.x()); - EXPECT_FLOAT_EQ(0.f, event.pressure); + EXPECT_FLOAT_EQ(0.f, event.pointer_details.radius_x); + EXPECT_FLOAT_EQ(0.f, event.pointer_details.force); // No dispatch on destruction. DestroyDevice(); @@ -740,12 +746,12 @@ TEST_F(TouchEventConverterEvdevTest, EXPECT_EQ(0, ev0.slot); EXPECT_EQ(999, ev0.location.x()); EXPECT_EQ(888, ev0.location.y()); - EXPECT_FLOAT_EQ(0.21568628f, ev0.pressure); + EXPECT_FLOAT_EQ(0.21568628f, ev0.pointer_details.force); EXPECT_EQ(1, ev1.slot); EXPECT_EQ(777, ev1.location.x()); EXPECT_EQ(666, ev1.location.y()); - EXPECT_FLOAT_EQ(0.17254902f, ev1.pressure); + EXPECT_FLOAT_EQ(0.17254902f, ev1.pointer_details.force); } // crbug.com/446939 @@ -966,13 +972,13 @@ TEST_F(TouchEventConverterEvdevTest, ActiveStylusTouchAndRelease) { EXPECT_EQ(ui::ET_TOUCH_PRESSED, event.type); EXPECT_EQ(9170, event.location.x()); EXPECT_EQ(3658, event.location.y()); - EXPECT_EQ(60.f / 1024, event.pressure); + EXPECT_EQ(60.f / 1024, event.pointer_details.force); event = dispatched_event(1); EXPECT_EQ(ui::ET_TOUCH_RELEASED, event.type); EXPECT_EQ(9173, event.location.x()); EXPECT_EQ(3906, event.location.y()); - EXPECT_EQ(0.f / 1024, event.pressure); + EXPECT_EQ(0.f / 1024, event.pointer_details.force); } TEST_F(TouchEventConverterEvdevTest, ActiveStylusMotion) { @@ -1012,25 +1018,33 @@ TEST_F(TouchEventConverterEvdevTest, ActiveStylusMotion) { EXPECT_EQ(ui::ET_TOUCH_PRESSED, event.type); EXPECT_EQ(8921, event.location.x()); EXPECT_EQ(1072, event.location.y()); - EXPECT_EQ(35.f / 1024, event.pressure); + EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN, + event.pointer_details.pointer_type); + EXPECT_EQ(35.f / 1024, event.pointer_details.force); event = dispatched_event(1); EXPECT_EQ(ui::ET_TOUCH_MOVED, event.type); EXPECT_EQ(8934, event.location.x()); EXPECT_EQ(981, event.location.y()); - EXPECT_EQ(184.f / 1024, event.pressure); + EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN, + event.pointer_details.pointer_type); + EXPECT_EQ(184.f / 1024, event.pointer_details.force); event = dispatched_event(2); EXPECT_EQ(ui::ET_TOUCH_MOVED, event.type); EXPECT_EQ(8930, event.location.x()); EXPECT_EQ(980, event.location.y()); - EXPECT_EQ(348.f / 1024, event.pressure); + EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN, + event.pointer_details.pointer_type); + EXPECT_EQ(348.f / 1024, event.pointer_details.force); event = dispatched_event(3); EXPECT_EQ(ui::ET_TOUCH_RELEASED, event.type); EXPECT_EQ(8930, event.location.x()); EXPECT_EQ(980, event.location.y()); - EXPECT_EQ(0.f / 1024, event.pressure); + EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN, + event.pointer_details.pointer_type); + EXPECT_EQ(0.f / 1024, event.pointer_details.force); } } // namespace ui diff --git a/chromium/ui/events/ozone/layout/layout_util.cc b/chromium/ui/events/ozone/layout/layout_util.cc index da0d5fedc28..5be53ecf48b 100644 --- a/chromium/ui/events/ozone/layout/layout_util.cc +++ b/chromium/ui/events/ozone/layout/layout_util.cc @@ -24,8 +24,6 @@ int ModifierDomKeyToEventFlag(DomKey key) { case DomKey::CONTROL: return EF_CONTROL_DOWN; case DomKey::META: - return EF_ALT_DOWN; - case DomKey::OS: return EF_COMMAND_DOWN; case DomKey::SHIFT: return EF_SHIFT_DOWN; diff --git a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc index aee22822030..0baefd46046 100644 --- a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc +++ b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc @@ -7,10 +7,13 @@ #include <stddef.h> #include <xkbcommon/xkbcommon-names.h> +#include <algorithm> + #include "base/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/free_deleter.h" #include "base/single_thread_task_runner.h" #include "base/task_runner.h" #include "base/thread_task_runner_handle.h" @@ -31,10 +34,15 @@ typedef base::Callback<void(const std::string&, scoped_ptr<char, base::FreeDeleter>)> LoadKeymapCallback; -KeyboardCode AlphanumericKeyboardCode(base::char16 character) { +KeyboardCode AlphanumericKeyboardCode(xkb_keysym_t xkb_keysym, + base::char16 character) { // Plain ASCII letters and digits map directly to VKEY values. - if ((character >= '0') && (character <= '9')) - return static_cast<KeyboardCode>(VKEY_0 + character - '0'); + if ((character >= '0') && (character <= '9')) { + int zero = ((xkb_keysym >= XKB_KEY_KP_0) && (xkb_keysym <= XKB_KEY_KP_9)) + ? VKEY_NUMPAD0 + : VKEY_0; + return static_cast<KeyboardCode>(zero + character - '0'); + } if ((character >= 'a') && (character <= 'z')) return static_cast<KeyboardCode>(VKEY_A + character - 'a'); if ((character >= 'A') && (character <= 'Z')) @@ -684,8 +692,15 @@ bool XkbKeyboardLayoutEngine::SetCurrentLayoutByName( true); return true; #else - // NOTIMPLEMENTED(); - return false; + // Required by ozone-wayland (at least) for non ChromeOS builds. See + // http://xkbcommon.org/doc/current/md_doc_quick-guide.html for further info. + xkb_keymap* keymap = xkb_keymap_new_from_string( + xkb_context_.get(), layout_name.c_str(), XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!keymap) + return false; + SetKeymap(keymap); + return true; #endif // defined(OS_CHROMEOS) } @@ -774,7 +789,7 @@ bool XkbKeyboardLayoutEngine::Lookup(DomCode dom_code, } *dom_key = DomKey::FromCharacter(character); - *key_code = AlphanumericKeyboardCode(character); + *key_code = AlphanumericKeyboardCode(xkb_keysym, character); if (*key_code == VKEY_UNKNOWN) { *key_code = DifficultKeyboardCode(dom_code, flags, xkb_keycode, xkb_flags, xkb_keysym, character); @@ -875,7 +890,7 @@ KeyboardCode XkbKeyboardLayoutEngine::DifficultKeyboardCode( return NonPrintableDomKeyToKeyboardCode(plain_key); // Plain ASCII letters and digits map directly to VKEY values. - KeyboardCode key_code = AlphanumericKeyboardCode(plain_character); + KeyboardCode key_code = AlphanumericKeyboardCode(xkb_keysym, plain_character); if (key_code != VKEY_UNKNOWN) return key_code; diff --git a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h index c485ecdc60e..ef52db750fa 100644 --- a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h +++ b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h @@ -7,9 +7,11 @@ #include <stdint.h> #include <xkbcommon/xkbcommon.h> + #include <vector> #include "base/containers/hash_tables.h" +#include "base/memory/free_deleter.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" diff --git a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc index ed59617ca8f..b8488af20d7 100644 --- a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc +++ b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc @@ -45,6 +45,7 @@ class VkTestXkbKeyboardLayoutEngine : public XkbKeyboardLayoutEngine { struct KeysymEntry { DomCode dom_code; xkb_keysym_t keysym; + base::char16 character; }; struct RuleNames { @@ -128,7 +129,7 @@ class VkTestXkbKeyboardLayoutEngine : public XkbKeyboardLayoutEngine { return false; } *xkb_keysym = keysym_entry_->keysym; - *character = 0; + *character = keysym_entry_->character; return true; } return false; @@ -815,6 +816,9 @@ TEST_F(XkbLayoutEngineVkTest, KeyboardCodeForNonPrintable) { {{DomCode::ENTER, XKB_KEY_Return}, VKEY_RETURN}, {{DomCode::NUMPAD_ENTER, XKB_KEY_KP_Enter}, VKEY_RETURN}, {{DomCode::SLEEP, XKB_KEY_XF86Sleep}, VKEY_SLEEP}, + // Verify that number pad digits produce located VKEY codes. + {{DomCode::NUMPAD0, XKB_KEY_KP_0, '0'}, VKEY_NUMPAD0}, + {{DomCode::NUMPAD9, XKB_KEY_KP_9, '9'}, VKEY_NUMPAD9}, }; for (const auto& e : kVkeyTestCase) { SCOPED_TRACE(static_cast<int>(e.test.dom_code)); diff --git a/chromium/ui/events/platform/x11/BUILD.gn b/chromium/ui/events/platform/x11/BUILD.gn index e55afc7ee6f..929613c7f7a 100644 --- a/chromium/ui/events/platform/x11/BUILD.gn +++ b/chromium/ui/events/platform/x11/BUILD.gn @@ -3,8 +3,9 @@ # found in the LICENSE file. import("//build/config/ui.gni") +import("//ui/ozone/ozone.gni") -assert(use_x11) +assert(use_x11 || ozone_platform_x11) component("x11") { output_name = "x11_events_platform" @@ -12,8 +13,6 @@ component("x11") { sources = [ "x11_event_source.cc", "x11_event_source.h", - "x11_event_source_glib.cc", - "x11_event_source_libevent.cc", "x11_hotplug_event_handler.cc", "x11_hotplug_event_handler.h", ] @@ -27,6 +26,7 @@ component("x11") { "//ui/events:events_base", "//ui/events/devices", "//ui/events/platform", + "//ui/events/x", "//ui/gfx/x", ] @@ -34,11 +34,17 @@ component("x11") { "//base", ] - if (is_linux) { - sources -= [ "x11_event_source_libevent.cc" ] + if (use_glib) { + sources += [ + "x11_event_source_glib.cc", + "x11_event_source_glib.h", + ] configs += [ "//build/config/linux:glib" ] } else { - sources -= [ "x11_event_source_glib.cc" ] + sources += [ + "x11_event_source_libevent.cc", + "x11_event_source_libevent.h", + ] } } diff --git a/chromium/ui/events/platform/x11/x11_event_source.cc b/chromium/ui/events/platform/x11/x11_event_source.cc index ba55e0b1751..e1bc15ecc31 100644 --- a/chromium/ui/events/platform/x11/x11_event_source.cc +++ b/chromium/ui/events/platform/x11/x11_event_source.cc @@ -4,51 +4,19 @@ #include "ui/events/platform/x11/x11_event_source.h" -#include <X11/extensions/XInput2.h> -#include <X11/X.h> -#include <X11/Xlib.h> #include <X11/XKBlib.h> +#include <X11/Xlib.h> #include "base/logging.h" #include "ui/events/devices/x11/device_data_manager_x11.h" #include "ui/events/event_utils.h" #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/events/platform/x11/x11_hotplug_event_handler.h" -#include "ui/gfx/x/x11_types.h" namespace ui { namespace { -int g_xinput_opcode = -1; - -bool InitializeXInput2(XDisplay* display) { - if (!display) - return false; - - int event, err; - - int xiopcode; - if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) { - DVLOG(1) << "X Input extension not available."; - return false; - } - g_xinput_opcode = xiopcode; - - int major = 2, minor = 2; - if (XIQueryVersion(display, &major, &minor) == BadRequest) { - DVLOG(1) << "XInput2 not supported in the server."; - return false; - } - if (major < 2 || (major == 2 && minor < 2)) { - DVLOG(1) << "XI version on server is " << major << "." << minor << ". " - << "But 2.2 is required."; - return false; - } - - return true; -} - bool InitializeXkb(XDisplay* display) { if (!display) return false; @@ -74,21 +42,29 @@ bool InitializeXkb(XDisplay* display) { } // namespace -X11EventSource::X11EventSource(XDisplay* display) - : display_(display), - continue_stream_(true) { - CHECK(display_); +X11EventSource* X11EventSource::instance_ = nullptr; + +X11EventSource::X11EventSource(X11EventSourceDelegate* delegate, + XDisplay* display) + : delegate_(delegate), display_(display), continue_stream_(true) { + DCHECK(!instance_); + instance_ = this; + + DCHECK(delegate_); + DCHECK(display_); DeviceDataManagerX11::CreateInstance(); - InitializeXInput2(display_); InitializeXkb(display_); } X11EventSource::~X11EventSource() { + DCHECK_EQ(this, instance_); + instance_ = nullptr; } // static X11EventSource* X11EventSource::GetInstance() { - return static_cast<X11EventSource*>(PlatformEventSource::GetInstance()); + DCHECK(instance_); + return instance_; } //////////////////////////////////////////////////////////////////////////////// @@ -118,25 +94,26 @@ void X11EventSource::BlockUntilWindowMapped(XID window) { } //////////////////////////////////////////////////////////////////////////////// -// X11EventSource, private +// X11EventSource, protected -uint32_t X11EventSource::ExtractCookieDataDispatchEvent(XEvent* xevent) { +void X11EventSource::ExtractCookieDataDispatchEvent(XEvent* xevent) { bool have_cookie = false; if (xevent->type == GenericEvent && XGetEventData(xevent->xgeneric.display, &xevent->xcookie)) { have_cookie = true; } - uint32_t action = DispatchEvent(xevent); + delegate_->ProcessXEvent(xevent); + PostDispatchEvent(xevent); if (have_cookie) XFreeEventData(xevent->xgeneric.display, &xevent->xcookie); - return action; } -uint32_t X11EventSource::DispatchEvent(XEvent* xevent) { - uint32_t action = PlatformEventSource::DispatchEvent(xevent); +void X11EventSource::PostDispatchEvent(XEvent* xevent) { if (xevent->type == GenericEvent && (xevent->xgeneric.evtype == XI_HierarchyChanged || - xevent->xgeneric.evtype == XI_DeviceChanged)) { + (xevent->xgeneric.evtype == XI_DeviceChanged && + static_cast<XIDeviceChangedEvent*>(xevent->xcookie.data)->reason == + XIDeviceChange))) { ui::UpdateDeviceList(); hotplug_event_handler_->OnHotplugEvent(); } @@ -147,7 +124,6 @@ uint32_t X11EventSource::DispatchEvent(XEvent* xevent) { // Clear stored scroll data ui::DeviceDataManagerX11::GetInstance()->InvalidateScrollClasses(); } - return action; } void X11EventSource::StopCurrentEventStream() { diff --git a/chromium/ui/events/platform/x11/x11_event_source.h b/chromium/ui/events/platform/x11/x11_event_source.h index 26d64601660..58169087e4e 100644 --- a/chromium/ui/events/platform/x11/x11_event_source.h +++ b/chromium/ui/events/platform/x11/x11_event_source.h @@ -10,11 +10,8 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "ui/events/events_export.h" -#include "ui/events/platform/platform_event_source.h" #include "ui/gfx/x/x11_types.h" -typedef struct _GPollFD GPollFD; -typedef struct _GSource GSource; typedef union _XEvent XEvent; typedef unsigned long XID; @@ -22,16 +19,30 @@ namespace ui { class X11HotplugEventHandler; -// A PlatformEventSource implementation for reading events from X11 server and -// dispatching the events to the appropriate dispatcher. -class EVENTS_EXPORT X11EventSource : public PlatformEventSource { +// Responsible for notifying X11EventSource when new XEvents are available and +// processing/dispatching XEvents. Implementations will likely be a +// PlatformEventSource. +class X11EventSourceDelegate { public: - explicit X11EventSource(XDisplay* display); - ~X11EventSource() override; + X11EventSourceDelegate() = default; + + // Processes (if necessary) and handles dispatching XEvents. + virtual void ProcessXEvent(XEvent* xevent) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(X11EventSourceDelegate); +}; + +// Receives X11 events and sends them to X11EventSourceDelegate. Handles +// receiving, pre-process and post-processing XEvents. +class EVENTS_EXPORT X11EventSource { + public: + X11EventSource(X11EventSourceDelegate* delegate, XDisplay* display); + ~X11EventSource(); static X11EventSource* GetInstance(); - // Called by the glib source dispatch function. Processes all (if any) + // Called when there is a new XEvent available. Processes all (if any) // available X events. void DispatchXEvents(); @@ -46,26 +57,31 @@ class EVENTS_EXPORT X11EventSource : public PlatformEventSource { // functions which require a mapped window. void BlockUntilWindowMapped(XID window); - protected: XDisplay* display() { return display_; } - private: + void StopCurrentEventStream(); + void OnDispatcherListChanged(); + + protected: // Extracts cookie data from |xevent| if it's of GenericType, and dispatches // the event. This function also frees up the cookie data after dispatch is // complete. - uint32_t ExtractCookieDataDispatchEvent(XEvent* xevent); + void ExtractCookieDataDispatchEvent(XEvent* xevent); + + // Handles updates after event has been dispatched. + void PostDispatchEvent(XEvent* xevent); + + private: + static X11EventSource* instance_; - // PlatformEventSource: - uint32_t DispatchEvent(XEvent* xevent) override; - void StopCurrentEventStream() override; - void OnDispatcherListChanged() override; + X11EventSourceDelegate* delegate_; // The connection to the X11 server used to receive the events. XDisplay* display_; // Keeps track of whether this source should continue to dispatch all the // available events. - bool continue_stream_; + bool continue_stream_ = true; scoped_ptr<X11HotplugEventHandler> hotplug_event_handler_; diff --git a/chromium/ui/events/platform/x11/x11_event_source_glib.cc b/chromium/ui/events/platform/x11/x11_event_source_glib.cc index 21516010811..1eb272a4f29 100644 --- a/chromium/ui/events/platform/x11/x11_event_source_glib.cc +++ b/chromium/ui/events/platform/x11/x11_event_source_glib.cc @@ -2,8 +2,7 @@ // 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/events/platform/x11/x11_event_source.h" +#include "ui/events/platform/x11/x11_event_source_glib.h" #include <glib.h> #include <X11/Xlib.h> @@ -48,52 +47,52 @@ GSourceFuncs XSourceFuncs = { NULL }; -class X11EventSourceGlib : public X11EventSource { - public: - explicit X11EventSourceGlib(XDisplay* display) - : X11EventSource(display), - x_source_(NULL) { - InitXSource(ConnectionNumber(display)); - } - - ~X11EventSourceGlib() override { - g_source_destroy(x_source_); - g_source_unref(x_source_); - } - - private: - void InitXSource(int fd) { - CHECK(!x_source_); - CHECK(display()) << "Unable to get connection to X server"; - - x_poll_.reset(new GPollFD()); - x_poll_->fd = fd; - x_poll_->events = G_IO_IN; - x_poll_->revents = 0; - - GLibX11Source* glib_x_source = static_cast<GLibX11Source*> - (g_source_new(&XSourceFuncs, sizeof(GLibX11Source))); - glib_x_source->display = display(); - glib_x_source->poll_fd = x_poll_.get(); - - x_source_ = glib_x_source; - g_source_add_poll(x_source_, x_poll_.get()); - g_source_set_can_recurse(x_source_, TRUE); - g_source_set_callback(x_source_, NULL, this, NULL); - g_source_attach(x_source_, g_main_context_default()); - } - - // The GLib event source for X events. - GSource* x_source_; - - // The poll attached to |x_source_|. - scoped_ptr<GPollFD> x_poll_; - - DISALLOW_COPY_AND_ASSIGN(X11EventSourceGlib); -}; - } // namespace +X11EventSourceGlib::X11EventSourceGlib(XDisplay* display) + : event_source_(this, display) { + InitXSource(ConnectionNumber(display)); +} + +X11EventSourceGlib::~X11EventSourceGlib() { + g_source_destroy(x_source_); + g_source_unref(x_source_); +} + +void X11EventSourceGlib::ProcessXEvent(XEvent* xevent) { + DispatchEvent(xevent); +} + +void X11EventSourceGlib::StopCurrentEventStream() { + event_source_.StopCurrentEventStream(); +} + +void X11EventSourceGlib::OnDispatcherListChanged() { + event_source_.OnDispatcherListChanged(); +} + +void X11EventSourceGlib::InitXSource(int fd) { + DCHECK(!x_source_); + DCHECK(event_source_.display()) << "Unable to get connection to X server"; + + x_poll_.reset(new GPollFD()); + x_poll_->fd = fd; + x_poll_->events = G_IO_IN; + x_poll_->revents = 0; + + GLibX11Source* glib_x_source = static_cast<GLibX11Source*>( + g_source_new(&XSourceFuncs, sizeof(GLibX11Source))); + glib_x_source->display = event_source_.display(); + glib_x_source->poll_fd = x_poll_.get(); + + x_source_ = glib_x_source; + g_source_add_poll(x_source_, x_poll_.get()); + g_source_set_can_recurse(x_source_, TRUE); + g_source_set_callback(x_source_, NULL, &event_source_, NULL); + g_source_attach(x_source_, g_main_context_default()); +} + +// static scoped_ptr<PlatformEventSource> PlatformEventSource::CreateDefault() { return make_scoped_ptr(new X11EventSourceGlib(gfx::GetXDisplay())); } diff --git a/chromium/ui/events/platform/x11/x11_event_source_glib.h b/chromium/ui/events/platform/x11/x11_event_source_glib.h new file mode 100644 index 00000000000..67a38b1b856 --- /dev/null +++ b/chromium/ui/events/platform/x11/x11_event_source_glib.h @@ -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. + +#ifndef UI_EVENTS_PLATFORM_X11_X11_EVENT_SOURCE_GLIB_H_ +#define UI_EVENTS_PLATFORM_X11_X11_EVENT_SOURCE_GLIB_H_ + +#include <stdint.h> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/events/events_export.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/x/x11_types.h" + +typedef struct _GPollFD GPollFD; +typedef struct _GSource GSource; + +namespace ui { + +// A PlatformEventSource implementation for X11. Dispatches XEvents and uses +// Glib to be notified for incoming XEvents. +class EVENTS_EXPORT X11EventSourceGlib : public X11EventSourceDelegate, + public PlatformEventSource { + public: + explicit X11EventSourceGlib(XDisplay* display); + ~X11EventSourceGlib() override; + + // X11EventSourceDelegate: + void ProcessXEvent(XEvent* xevent) override; + + private: + // PlatformEventSource: + void StopCurrentEventStream() override; + void OnDispatcherListChanged() override; + + void InitXSource(int fd); + + X11EventSource event_source_; + + // The GLib event source for X events. + GSource* x_source_ = nullptr; + + // The poll attached to |x_source_|. + scoped_ptr<GPollFD> x_poll_; + + DISALLOW_COPY_AND_ASSIGN(X11EventSourceGlib); +}; + +} // namespace ui + +#endif // UI_EVENTS_PLATFORM_X11_X11_EVENT_SOURCE_GLIB_H_ diff --git a/chromium/ui/events/platform/x11/x11_event_source_libevent.cc b/chromium/ui/events/platform/x11/x11_event_source_libevent.cc index a3ff7a50f94..cf0e3d648f7 100644 --- a/chromium/ui/events/platform/x11/x11_event_source_libevent.cc +++ b/chromium/ui/events/platform/x11/x11_event_source_libevent.cc @@ -2,67 +2,198 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/events/platform/x11/x11_event_source_libevent.h" #include <X11/Xlib.h> +#include <X11/extensions/XInput2.h> -#include "base/macros.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_pump_libevent.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/x/events_x_utils.h" namespace ui { namespace { -class X11EventSourceLibevent : public X11EventSource, - public base::MessagePumpLibevent::Watcher { - public: - explicit X11EventSourceLibevent(XDisplay* display) - : X11EventSource(display), - initialized_(false) { - AddEventWatcher(); +// Translates XI2 XEvent into a ui::Event. +scoped_ptr<ui::Event> TranslateXI2EventToEvent(const XEvent& xev) { + EventType event_type = EventTypeFromXEvent(xev); + switch (event_type) { + case ET_KEY_PRESSED: + case ET_KEY_RELEASED: + return make_scoped_ptr(new KeyEvent(event_type, + KeyboardCodeFromXKeyEvent(&xev), + EventFlagsFromXEvent(xev))); + case ET_MOUSE_PRESSED: + case ET_MOUSE_MOVED: + case ET_MOUSE_DRAGGED: + case ET_MOUSE_RELEASED: + return make_scoped_ptr( + new MouseEvent(event_type, EventLocationFromXEvent(xev), + EventSystemLocationFromXEvent(xev), + EventTimeFromXEvent(xev), EventFlagsFromXEvent(xev), + GetChangedMouseButtonFlagsFromXEvent(xev))); + case ET_MOUSEWHEEL: + return make_scoped_ptr(new MouseWheelEvent( + GetMouseWheelOffsetFromXEvent(xev), EventLocationFromXEvent(xev), + EventSystemLocationFromXEvent(xev), EventTimeFromXEvent(xev), + EventFlagsFromXEvent(xev), + GetChangedMouseButtonFlagsFromXEvent(xev))); + case ET_SCROLL_FLING_START: + case ET_SCROLL_FLING_CANCEL: { + float x_offset, y_offset, x_offset_ordinal, y_offset_ordinal; + GetFlingDataFromXEvent(xev, &x_offset, &y_offset, &x_offset_ordinal, + &y_offset_ordinal, nullptr); + return make_scoped_ptr(new ScrollEvent( + event_type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev), + EventFlagsFromXEvent(xev), x_offset, y_offset, x_offset_ordinal, + y_offset_ordinal, 0)); + } + case ET_SCROLL: { + float x_offset, y_offset, x_offset_ordinal, y_offset_ordinal; + int finger_count; + GetScrollOffsetsFromXEvent(xev, &x_offset, &y_offset, &x_offset_ordinal, + &y_offset_ordinal, &finger_count); + return make_scoped_ptr(new ScrollEvent( + event_type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev), + EventFlagsFromXEvent(xev), x_offset, y_offset, x_offset_ordinal, + y_offset_ordinal, finger_count)); + } + case ET_TOUCH_MOVED: + case ET_TOUCH_PRESSED: + case ET_TOUCH_CANCELLED: + case ET_TOUCH_RELEASED: + return make_scoped_ptr( + new TouchEvent(event_type, EventLocationFromXEvent(xev), + GetTouchIdFromXEvent(xev), EventTimeFromXEvent(xev))); + case ET_UNKNOWN: + return nullptr; + default: + break; } + return nullptr; +} - ~X11EventSourceLibevent() override { +// Translates a XEvent into a ui::Event. +scoped_ptr<ui::Event> TranslateXEventToEvent(const XEvent& xev) { + int flags = EventFlagsFromXEvent(xev); + switch (xev.type) { + case LeaveNotify: + case EnterNotify: + // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is + // not real mouse move event. + if (xev.type == EnterNotify) + flags |= EF_IS_SYNTHESIZED; + return make_scoped_ptr( + new MouseEvent(ET_MOUSE_MOVED, EventLocationFromXEvent(xev), + EventSystemLocationFromXEvent(xev), + EventTimeFromXEvent(xev), flags, 0)); + + case KeyPress: + case KeyRelease: + return make_scoped_ptr(new KeyEvent( + EventTypeFromXEvent(xev), KeyboardCodeFromXKeyEvent(&xev), flags)); + + case ButtonPress: + case ButtonRelease: { + switch (EventTypeFromXEvent(xev)) { + case ET_MOUSEWHEEL: + return make_scoped_ptr(new MouseWheelEvent( + GetMouseWheelOffsetFromXEvent(xev), EventLocationFromXEvent(xev), + EventSystemLocationFromXEvent(xev), EventTimeFromXEvent(xev), + flags, 0)); + case ET_MOUSE_PRESSED: + case ET_MOUSE_RELEASED: + return make_scoped_ptr(new MouseEvent( + EventTypeFromXEvent(xev), EventLocationFromXEvent(xev), + EventSystemLocationFromXEvent(xev), EventTimeFromXEvent(xev), + flags, GetChangedMouseButtonFlagsFromXEvent(xev))); + case ET_UNKNOWN: + // No event is created for X11-release events for mouse-wheel + // buttons. + break; + default: + NOTREACHED(); + } + break; + } + + case GenericEvent: + return TranslateXI2EventToEvent(xev); } + return nullptr; +} - private: - void AddEventWatcher() { - if (initialized_) - return; - if (!base::MessageLoop::current()) - return; - - int fd = ConnectionNumber(display()); - base::MessageLoopForUI::current()->WatchFileDescriptor(fd, true, - base::MessagePumpLibevent::WATCH_READ, &watcher_controller_, this); - initialized_ = true; - } +} // namespace - // PlatformEventSource: - void OnDispatcherListChanged() override { - AddEventWatcher(); - } +X11EventSourceLibevent::X11EventSourceLibevent(XDisplay* display) + : event_source_(this, display) { + AddEventWatcher(); +} + +X11EventSourceLibevent::~X11EventSourceLibevent() {} - // base::MessagePumpLibevent::Watcher: - void OnFileCanReadWithoutBlocking(int fd) override { - DispatchXEvents(); +void X11EventSourceLibevent::AddXEventDispatcher(XEventDispatcher* dispatcher) { + dispatchers_xevent_.AddObserver(dispatcher); +} + +void X11EventSourceLibevent::RemoveXEventDispatcher( + XEventDispatcher* dispatcher) { + dispatchers_xevent_.RemoveObserver(dispatcher); +} + +void X11EventSourceLibevent::ProcessXEvent(XEvent* xevent) { + scoped_ptr<ui::Event> translated_event = TranslateXEventToEvent(*xevent); + if (translated_event) { + DispatchEvent(translated_event.get()); + } else { + // Only if we can't translate XEvent into ui::Event, try to dispatch XEvent + // directly to XEventDispatchers. + DispatchXEventToXEventDispatchers(xevent); } +} - void OnFileCanWriteWithoutBlocking(int fd) override { - NOTREACHED(); +void X11EventSourceLibevent::AddEventWatcher() { + if (initialized_) + return; + if (!base::MessageLoop::current()) + return; + + int fd = ConnectionNumber(event_source_.display()); + base::MessageLoopForUI::current()->WatchFileDescriptor( + fd, true, base::MessagePumpLibevent::WATCH_READ, &watcher_controller_, + this); + initialized_ = true; +} + +void X11EventSourceLibevent::DispatchXEventToXEventDispatchers(XEvent* xevent) { + if (dispatchers_xevent_.might_have_observers()) { + base::ObserverList<XEventDispatcher>::Iterator iter(&dispatchers_xevent_); + while (XEventDispatcher* dispatcher = iter.GetNext()) { + if (dispatcher->DispatchXEvent(xevent)) + break; + } } +} - base::MessagePumpLibevent::FileDescriptorWatcher watcher_controller_; - bool initialized_; +void X11EventSourceLibevent::StopCurrentEventStream() { + event_source_.StopCurrentEventStream(); +} - DISALLOW_COPY_AND_ASSIGN(X11EventSourceLibevent); -}; +void X11EventSourceLibevent::OnDispatcherListChanged() { + AddEventWatcher(); + event_source_.OnDispatcherListChanged(); +} -} // namespace +void X11EventSourceLibevent::OnFileCanReadWithoutBlocking(int fd) { + event_source_.DispatchXEvents(); +} -scoped_ptr<PlatformEventSource> PlatformEventSource::CreateDefault() { - return make_scoped_ptr(new X11EventSourceLibevent(gfx::GetXDisplay())); +void X11EventSourceLibevent::OnFileCanWriteWithoutBlocking(int fd) { + NOTREACHED(); } } // namespace ui diff --git a/chromium/ui/events/platform/x11/x11_event_source_libevent.h b/chromium/ui/events/platform/x11/x11_event_source_libevent.h new file mode 100644 index 00000000000..b9f2eba9c8d --- /dev/null +++ b/chromium/ui/events/platform/x11/x11_event_source_libevent.h @@ -0,0 +1,79 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_PLATFORM_X11_X11_EVENT_SOURCE_LIBEVENT_H_ +#define UI_EVENTS_PLATFORM_X11_X11_EVENT_SOURCE_LIBEVENT_H_ + +#include "base/macros.h" +#include "base/message_loop/message_pump_libevent.h" +#include "ui/events/events_export.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/x11/x11_event_source.h" + +namespace ui { + +// Interface for classes that want to receive XEvent directly. Only used with +// Ozone X11 currently and only events that can't be translated into ui::Events +// are sent via this path. +class EVENTS_EXPORT XEventDispatcher { + public: + // Sends XEvent to XEventDispatcher for handling. Returns true if the XEvent + // was dispatched, otherwise false. After the first XEventDispatcher returns + // true XEvent dispatching stops. + virtual bool DispatchXEvent(XEvent* xevent) = 0; + + protected: + virtual ~XEventDispatcher() {} +}; + +// A PlatformEventSource implementation for Ozone X11. Converts XEvents to +// ui::Events before dispatching. For X11 specific events a separate list of +// XEventDispatchers is maintained. Uses Libevent to be notified for incoming +// XEvents. +class EVENTS_EXPORT X11EventSourceLibevent + : public X11EventSourceDelegate, + public PlatformEventSource, + public base::MessagePumpLibevent::Watcher { + public: + explicit X11EventSourceLibevent(XDisplay* display); + ~X11EventSourceLibevent() override; + + // Adds a XEvent dispatcher to the XEvent dispatcher list. + void AddXEventDispatcher(XEventDispatcher* dispatcher); + + // Removes a XEvent dispatcher fERrom the XEvent dispatcher list. + void RemoveXEventDispatcher(XEventDispatcher* dispatcher); + + // X11EventSourceDelegate: + void ProcessXEvent(XEvent* xevent) override; + + private: + // Registers event watcher with Libevent. + void AddEventWatcher(); + + // Sends XEvent to registered XEventDispatchers. + void DispatchXEventToXEventDispatchers(XEvent* xevent); + + // PlatformEventSource: + void StopCurrentEventStream() override; + void OnDispatcherListChanged() override; + + // base::MessagePumpLibevent::Watcher: + void OnFileCanReadWithoutBlocking(int fd) override; + void OnFileCanWriteWithoutBlocking(int fd) override; + + X11EventSource event_source_; + + // Keep track of all XEventDispatcher to send XEvents directly to. + base::ObserverList<XEventDispatcher> dispatchers_xevent_; + + base::MessagePumpLibevent::FileDescriptorWatcher watcher_controller_; + bool initialized_ = false; + + DISALLOW_COPY_AND_ASSIGN(X11EventSourceLibevent); +}; + +} // namespace ui + +#endif // UI_EVENTS_PLATFORM_X11_X11_EVENT_SOURCE_LIBEVENT_H_ diff --git a/chromium/ui/events/platform/x11/x11_events_platform.gyp b/chromium/ui/events/platform/x11/x11_events_platform.gyp index cb1bbcd39c0..a62472227af 100644 --- a/chromium/ui/events/platform/x11/x11_events_platform.gyp +++ b/chromium/ui/events/platform/x11/x11_events_platform.gyp @@ -20,13 +20,12 @@ '../../devices/events_devices.gyp:events_devices', '../../events.gyp:events', '../../events.gyp:events_base', + '../../x/events_x.gyp:events_x', '../events_platform.gyp:events_platform', ], 'sources': [ 'x11_event_source.cc', 'x11_event_source.h', - 'x11_event_source_glib.cc', - 'x11_event_source_libevent.cc', 'x11_hotplug_event_handler.cc', 'x11_hotplug_event_handler.h', ], @@ -35,13 +34,15 @@ 'dependencies': [ '../../../../build/linux/system.gyp:glib', ], - 'sources!': [ - 'x11_event_source_libevent.cc', + 'sources': [ + 'x11_event_source_glib.cc', + 'x11_event_source_glib.h', ], }, { # use_glib == 0 - 'sources!': [ - 'x11_event_source_glib.cc', + 'sources': [ + 'x11_event_source_libevent.cc', + 'x11_event_source_libevent.h', ], }], ], diff --git a/chromium/ui/events/win/event_utils_win_unittest.cc b/chromium/ui/events/win/event_utils_win_unittest.cc new file mode 100644 index 00000000000..a4efce4fa51 --- /dev/null +++ b/chromium/ui/events/win/event_utils_win_unittest.cc @@ -0,0 +1,66 @@ +// 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/events/event_utils.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/win/window_impl.h" + +namespace ui { + +namespace { + +class TestWindow : public gfx::WindowImpl { + public: + TestWindow() {} + ~TestWindow() override {} + + BOOL ProcessWindowMessage(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT& result, + DWORD msg_map_id = 0) override { + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestWindow); +}; + +MSG CreateEvent(UINT type, WORD x, WORD y, HWND hwnd) { + MSG event; + event.message = type; + event.hwnd = hwnd; + event.lParam = MAKELPARAM(x, y); + return event; +} + +TEST(EventWinTest, EventSystemLocationFromNative) { + TestWindow test_window; + const WORD x_coord = 10; + const WORD y_coord = 20; + const WORD x_window_offset = 100; + const WORD y_window_offset = 100; + test_window.Init(nullptr, + gfx::Rect(x_window_offset, y_window_offset, 100, 100)); + EXPECT_TRUE(test_window.hwnd() != nullptr); + + { + MSG event = + CreateEvent(WM_MOUSEWHEEL, x_coord, y_coord, test_window.hwnd()); + // Mouse wheel events already have screen coordinates so they should not be + // converted. + EXPECT_EQ(gfx::Point(x_coord, y_coord), + EventSystemLocationFromNative(event)); + } + + MSG event = CreateEvent(WM_LBUTTONDOWN, x_coord, y_coord, test_window.hwnd()); + EXPECT_EQ(gfx::Point(x_coord + x_window_offset, y_coord + y_window_offset), + EventSystemLocationFromNative(event)); +} + +} // namespace + +} // namespace ui diff --git a/chromium/ui/events/win/events_win.cc b/chromium/ui/events/win/events_win.cc index 20c6e61b6ae..49b67400edd 100644 --- a/chromium/ui/events/win/events_win.cc +++ b/chromium/ui/events/win/events_win.cc @@ -260,7 +260,9 @@ gfx::Point EventSystemLocationFromNative( const base::NativeEvent& native_event) { POINT global_point = { static_cast<short>(LOWORD(native_event.lParam)), static_cast<short>(HIWORD(native_event.lParam)) }; - ClientToScreen(native_event.hwnd, &global_point); + // Wheel events have position in screen coordinates. + if (!IsMouseWheelEvent(native_event)) + ClientToScreen(native_event.hwnd, &global_point); return gfx::Point(global_point); } @@ -322,24 +324,20 @@ int GetTouchId(const base::NativeEvent& xev) { return 0; } -float GetTouchRadiusX(const base::NativeEvent& native_event) { - NOTIMPLEMENTED(); - return 1.0; -} - -float GetTouchRadiusY(const base::NativeEvent& native_event) { - NOTIMPLEMENTED(); - return 1.0; -} - float GetTouchAngle(const base::NativeEvent& native_event) { NOTIMPLEMENTED(); return 0.0; } -float GetTouchForce(const base::NativeEvent& native_event) { +PointerDetails GetTouchPointerDetailsFromNative( + const base::NativeEvent& native_event) { NOTIMPLEMENTED(); - return 0.0; + return PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, + /* radius_x */ 1.0, + /* radius_y */ 1.0, + /* force */ 0.f, + /* tilt_x */ 0.f, + /* tilt_y */ 0.f); } bool GetScrollOffsets(const base::NativeEvent& native_event, diff --git a/chromium/ui/events/x/BUILD.gn b/chromium/ui/events/x/BUILD.gn index 23299469601..991ce6ec8c3 100644 --- a/chromium/ui/events/x/BUILD.gn +++ b/chromium/ui/events/x/BUILD.gn @@ -16,8 +16,5 @@ component("x") { "//ui/events/devices", "//ui/gfx/x", ] - configs += [ - "//build/config/linux:glib", - "//build/config/linux:x11", - ] + configs += [ "//build/config/linux:x11" ] } diff --git a/chromium/ui/events/x/events_x.cc b/chromium/ui/events/x/events_x.cc index 0f27922e090..9432a606b65 100644 --- a/chromium/ui/events/x/events_x.cc +++ b/chromium/ui/events/x/events_x.cc @@ -154,20 +154,18 @@ int GetTouchId(const base::NativeEvent& native_event) { return GetTouchIdFromXEvent(*native_event); } -float GetTouchRadiusX(const base::NativeEvent& native_event) { - return GetTouchRadiusXFromXEvent(*native_event); -} - -float GetTouchRadiusY(const base::NativeEvent& native_event) { - return GetTouchRadiusYFromXEvent(*native_event); -} - float GetTouchAngle(const base::NativeEvent& native_event) { return GetTouchAngleFromXEvent(*native_event); } -float GetTouchForce(const base::NativeEvent& native_event) { - return GetTouchForceFromXEvent(*native_event); +PointerDetails GetTouchPointerDetailsFromNative( + const base::NativeEvent& native_event) { + return PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, + GetTouchRadiusXFromXEvent(*native_event), + GetTouchRadiusYFromXEvent(*native_event), + GetTouchForceFromXEvent(*native_event), + /* tilt_x */ 0.f, + /* tilt_y */ 0.f); } bool GetScrollOffsets(const base::NativeEvent& native_event, diff --git a/chromium/ui/events/x/events_x_unittest.cc b/chromium/ui/events/x/events_x_unittest.cc index 3073c8aa8d9..42f38a30e5b 100644 --- a/chromium/ui/events/x/events_x_unittest.cc +++ b/chromium/ui/events/x/events_x_unittest.cc @@ -241,9 +241,11 @@ TEST_F(EventsXTest, TouchEventBasic) { EXPECT_EQ(ui::ET_TOUCH_PRESSED, ui::EventTypeFromNative(scoped_xevent)); EXPECT_EQ("10,10", ui::EventLocationFromNative(scoped_xevent).ToString()); EXPECT_EQ(GetTouchId(scoped_xevent), 0); - EXPECT_EQ(GetTouchRadiusX(scoped_xevent), 10); EXPECT_FLOAT_EQ(GetTouchAngle(scoped_xevent), 0.15f); - EXPECT_FLOAT_EQ(GetTouchForce(scoped_xevent), 0.1f); + PointerDetails pointer_details = + GetTouchPointerDetailsFromNative(scoped_xevent); + EXPECT_FLOAT_EQ(pointer_details.radius_x, 10.0f); + EXPECT_FLOAT_EQ(pointer_details.force, 0.1f); // Touch update, with new orientation info. valuators.clear(); @@ -254,9 +256,10 @@ TEST_F(EventsXTest, TouchEventBasic) { EXPECT_EQ(ui::ET_TOUCH_MOVED, ui::EventTypeFromNative(scoped_xevent)); EXPECT_EQ("20,20", ui::EventLocationFromNative(scoped_xevent).ToString()); EXPECT_EQ(GetTouchId(scoped_xevent), 0); - EXPECT_EQ(GetTouchRadiusX(scoped_xevent), 10); EXPECT_FLOAT_EQ(GetTouchAngle(scoped_xevent), 0.25f); - EXPECT_FLOAT_EQ(GetTouchForce(scoped_xevent), 0.1f); + pointer_details = GetTouchPointerDetailsFromNative(scoped_xevent); + EXPECT_FLOAT_EQ(pointer_details.radius_x, 10.0f); + EXPECT_FLOAT_EQ(pointer_details.force, 0.1f); // Another touch with tracking id 6, touch id 1. valuators.clear(); @@ -269,9 +272,10 @@ TEST_F(EventsXTest, TouchEventBasic) { EXPECT_EQ(ui::ET_TOUCH_PRESSED, ui::EventTypeFromNative(scoped_xevent)); EXPECT_EQ("200,200", ui::EventLocationFromNative(scoped_xevent).ToString()); EXPECT_EQ(GetTouchId(scoped_xevent), 1); - EXPECT_EQ(GetTouchRadiusX(scoped_xevent), 50); EXPECT_FLOAT_EQ(GetTouchAngle(scoped_xevent), 0.45f); - EXPECT_FLOAT_EQ(GetTouchForce(scoped_xevent), 0.5f); + pointer_details = GetTouchPointerDetailsFromNative(scoped_xevent); + EXPECT_FLOAT_EQ(pointer_details.radius_x, 50.0f); + EXPECT_FLOAT_EQ(pointer_details.force, 0.5f); // Touch with tracking id 5 should have old radius/angle value and new pressue // value. @@ -282,9 +286,10 @@ TEST_F(EventsXTest, TouchEventBasic) { EXPECT_EQ(ui::ET_TOUCH_RELEASED, ui::EventTypeFromNative(scoped_xevent)); EXPECT_EQ("30,30", ui::EventLocationFromNative(scoped_xevent).ToString()); EXPECT_EQ(GetTouchId(scoped_xevent), 0); - EXPECT_EQ(GetTouchRadiusX(scoped_xevent), 10); EXPECT_FLOAT_EQ(GetTouchAngle(scoped_xevent), 0.25f); - EXPECT_FLOAT_EQ(GetTouchForce(scoped_xevent), 0.05f); + pointer_details = GetTouchPointerDetailsFromNative(scoped_xevent); + EXPECT_FLOAT_EQ(pointer_details.radius_x, 10.0f); + EXPECT_FLOAT_EQ(pointer_details.force, 0.05f); // Touch with tracking id 6 should have old angle/pressure value and new // radius value. @@ -295,9 +300,10 @@ TEST_F(EventsXTest, TouchEventBasic) { EXPECT_EQ(ui::ET_TOUCH_RELEASED, ui::EventTypeFromNative(scoped_xevent)); EXPECT_EQ("200,200", ui::EventLocationFromNative(scoped_xevent).ToString()); EXPECT_EQ(GetTouchId(scoped_xevent), 1); - EXPECT_EQ(GetTouchRadiusX(scoped_xevent), 25); EXPECT_FLOAT_EQ(GetTouchAngle(scoped_xevent), 0.45f); - EXPECT_FLOAT_EQ(GetTouchForce(scoped_xevent), 0.5f); + pointer_details = GetTouchPointerDetailsFromNative(scoped_xevent); + EXPECT_FLOAT_EQ(pointer_details.radius_x, 25.0f); + EXPECT_FLOAT_EQ(pointer_details.force, 0.5f); } int GetTouchIdForTrackingId(uint32_t tracking_id) { diff --git a/chromium/ui/events/x/events_x_utils.cc b/chromium/ui/events/x/events_x_utils.cc index 452c096a06a..64b652c7cfc 100644 --- a/chromium/ui/events/x/events_x_utils.cc +++ b/chromium/ui/events/x/events_x_utils.cc @@ -626,15 +626,22 @@ gfx::Vector2d GetMouseWheelOffsetFromXEvent(const XEvent& xev) { int button = xev.type == GenericEvent ? EventButtonFromXEvent(xev) : xev.xbutton.button; + // If this is an xinput1 scroll event from an xinput2 mouse then we need to + // block the legacy scroll events for the necessary axes. + int scroll_class_type = + DeviceDataManagerX11::GetInstance()->GetScrollClassDeviceDetail(xev); + bool xi2_vertical = scroll_class_type & SCROLL_TYPE_VERTICAL; + bool xi2_horizontal = scroll_class_type & SCROLL_TYPE_HORIZONTAL; + switch (button) { case 4: - return gfx::Vector2d(0, kWheelScrollAmount); + return gfx::Vector2d(0, xi2_vertical ? 0 : kWheelScrollAmount); case 5: - return gfx::Vector2d(0, -kWheelScrollAmount); + return gfx::Vector2d(0, xi2_vertical ? 0 : -kWheelScrollAmount); case 6: - return gfx::Vector2d(kWheelScrollAmount, 0); + return gfx::Vector2d(xi2_horizontal ? 0 : kWheelScrollAmount, 0); case 7: - return gfx::Vector2d(-kWheelScrollAmount, 0); + return gfx::Vector2d(xi2_horizontal ? 0 : -kWheelScrollAmount, 0); default: return gfx::Vector2d(); } @@ -731,7 +738,7 @@ bool GetScrollOffsetsFromXEvent(const XEvent& xev, return true; } - if (DeviceDataManagerX11::GetInstance()->GetScrollClassDeviceDetail(xev) != + if (DeviceDataManagerX11::GetInstance()->GetScrollClassEventDetail(xev) != SCROLL_TYPE_NO_SCROLL) { double x_scroll_offset, y_scroll_offset; DeviceDataManagerX11::GetInstance()->GetScrollClassOffsets( diff --git a/chromium/ui/events/x/events_x_utils.h b/chromium/ui/events/x/events_x_utils.h index 15c4e387f82..0a07061ee17 100644 --- a/chromium/ui/events/x/events_x_utils.h +++ b/chromium/ui/events/x/events_x_utils.h @@ -16,16 +16,16 @@ typedef union _XEvent XEvent; namespace ui { -// Get the EventType from a XEvent. +// Gets the EventType from a XEvent. EVENTS_X_EXPORT EventType EventTypeFromXEvent(const XEvent& xev); -// Get the EventFlags from a XEvent. +// Gets the EventFlags from a XEvent. EVENTS_X_EXPORT int EventFlagsFromXEvent(const XEvent& xev); -// Get the timestamp from a XEvent. +// Gets the timestamp from a XEvent. EVENTS_X_EXPORT base::TimeDelta EventTimeFromXEvent(const XEvent& xev); -// Get the location from a XEvent. The coordinate system of the resultant +// Gets the location from a XEvent. The coordinate system of the resultant // |Point| has the origin at top-left of the "root window". The nature of // this "root window" and how it maps to platform-specific drawing surfaces is // defined in ui/aura/root_window.* and ui/aura/window_tree_host*. diff --git a/chromium/ui/file_manager/audio_player/js/compiled_resources.gyp b/chromium/ui/file_manager/audio_player/js/compiled_resources.gyp index 9fd6e654008..98f6fa341fb 100644 --- a/chromium/ui/file_manager/audio_player/js/compiled_resources.gyp +++ b/chromium/ui/file_manager/audio_player/js/compiled_resources.gyp @@ -15,6 +15,7 @@ # Referenced in common/js/util.js. '../../../webui/resources/js/cr/ui/dialogs.js', '../../../webui/resources/js/load_time_data.js', + '../../../webui/resources/js/promise_resolver.js', '../../../webui/resources/js/util.js', '../../file_manager/common/js/util.js', '../../file_manager/common/js/async_util.js', @@ -66,6 +67,7 @@ '../../../webui/resources/js/cr/ui/array_data_model.js', '../../../webui/resources/js/cr/ui/dialogs.js', '../../../webui/resources/js/load_time_data.js', + '../../../webui/resources/js/promise_resolver.js', '../../../webui/resources/js/util.js', '../../file_manager/common/js/async_util.js', '../../file_manager/common/js/file_type.js', @@ -88,6 +90,7 @@ '../../file_manager/background/js/volume_manager.js', '../../file_manager/foreground/js/volume_manager_wrapper.js', '../elements/control_panel.js', + '../elements/track_info_panel.js', '../elements/track_list.js', '../elements/audio_player.js', ], @@ -109,4 +112,3 @@ } ], } - diff --git a/chromium/ui/file_manager/file_manager/background/js/compiled_resources.gyp b/chromium/ui/file_manager/file_manager/background/js/compiled_resources.gyp index f1b735fa219..8232bc9155c 100644 --- a/chromium/ui/file_manager/file_manager/background/js/compiled_resources.gyp +++ b/chromium/ui/file_manager/file_manager/background/js/compiled_resources.gyp @@ -11,6 +11,7 @@ '../../../../webui/resources/js/load_time_data.js', '../../../../webui/resources/js/cr.js', '../../../../webui/resources/js/cr/ui.js', + '../../../../webui/resources/js/promise_resolver.js', '../../../../webui/resources/js/util.js', '../../../../webui/resources/js/cr/event_target.js', '../../../../webui/resources/js/cr/ui/array_data_model.js', diff --git a/chromium/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp b/chromium/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp index 5e8a0ac4e5f..8fd66ac5f4e 100644 --- a/chromium/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp +++ b/chromium/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp @@ -11,6 +11,7 @@ '../../../../../third_party/jstemplate/compiled_resources.gyp:jstemplate', '../../../../../ui/webui/resources/js/load_time_data.js', '../../../../../ui/webui/resources/js/cr.js', + '../../../../../ui/webui/resources/js/promise_resolver.js', '../../../../../ui/webui/resources/js/util.js', '../../../../../ui/webui/resources/js/compiled_resources.gyp:i18n_template_no_process', '../../../../../ui/webui/resources/js/event_tracker.js', @@ -84,6 +85,7 @@ './directory_contents.js', './directory_model.js', './directory_tree_naming_controller.js', + './elements_importer.js', './empty_folder_controller.js', './file_manager.js', './file_manager_commands.js', @@ -122,6 +124,7 @@ './ui/actions_submenu.js', './ui/banners.js', './ui/default_task_dialog.js', + './ui/details_container.js', './ui/dialog_footer.js', './ui/directory_tree.js', './ui/drag_selector.js', @@ -130,18 +133,23 @@ './ui/file_grid.js', './ui/file_list_selection_model.js', './ui/file_manager_ui.js', + './ui/file_metadata_formatter.js', './ui/file_table.js', './ui/file_table_list.js', + './ui/files_alert_dialog.js', + './ui/files_confirm_dialog.js', './ui/files_menu.js', './ui/gear_menu.js', './ui/list_container.js', './ui/location_line.js', + './ui/multi_file_details.js', './ui/multi_profile_share_dialog.js', './ui/progress_center_panel.js', './ui/providers_menu.js', './ui/scrollbar.js', './ui/search_box.js', './ui/share_dialog.js', + './ui/single_file_details.js', './ui/suggest_apps_dialog.js', './main_window_component.js', './volume_manager_wrapper.js', diff --git a/chromium/ui/file_manager/gallery/js/compiled_resources.gyp b/chromium/ui/file_manager/gallery/js/compiled_resources.gyp index 6a328428593..45747b5781a 100644 --- a/chromium/ui/file_manager/gallery/js/compiled_resources.gyp +++ b/chromium/ui/file_manager/gallery/js/compiled_resources.gyp @@ -14,6 +14,7 @@ '../../../webui/resources/js/cr/ui/array_data_model.js', '../../../webui/resources/js/cr/ui/dialogs.js', '../../../webui/resources/js/load_time_data.js', + '../../../webui/resources/js/promise_resolver.js', '../../../webui/resources/js/util.js', '../../file_manager/common/js/util.js', '../../file_manager/common/js/async_util.js', @@ -64,6 +65,7 @@ '../../../../third_party/polymer/v1_0/components-chromium/paper-input/paper-input-extracted.js', '../../../../third_party/polymer/v1_0/components-chromium/paper-progress/paper-progress-extracted.js', '../../../../third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js', + '../../../webui/resources/js/promise_resolver.js', '../../../webui/resources/js/util.js', '../../../webui/resources/js/event_tracker.js', '../../../webui/resources/js/load_time_data.js', @@ -104,6 +106,7 @@ '../../file_manager/foreground/js/share_client.js', '../../file_manager/foreground/js/thumbnail_loader.js', '../../file_manager/foreground/js/ui/file_manager_dialog_base.js', + '../../file_manager/foreground/js/ui/files_confirm_dialog.js', '../../file_manager/foreground/js/ui/share_dialog.js', '../../file_manager/foreground/js/volume_manager_wrapper.js', '../../file_manager/background/js/volume_manager.js', diff --git a/chromium/ui/file_manager/video_player/js/compiled_resources.gyp b/chromium/ui/file_manager/video_player/js/compiled_resources.gyp index 5123864cad1..749d44b1eb9 100644 --- a/chromium/ui/file_manager/video_player/js/compiled_resources.gyp +++ b/chromium/ui/file_manager/video_player/js/compiled_resources.gyp @@ -14,6 +14,7 @@ '../../../webui/resources/js/cr/ui/array_data_model.js', # Referenced in common/js/util.js. '../../../webui/resources/js/cr/ui/dialogs.js', + '../../../webui/resources/js/promise_resolver.js', '../../../webui/resources/js/load_time_data.js', '../../../webui/resources/js/util.js', '../../file_manager/common/js/util.js', @@ -66,6 +67,7 @@ '../../../webui/resources/js/cr.js', '../../../webui/resources/js/load_time_data.js', '../../../webui/resources/js/event_tracker.js', + '../../../webui/resources/js/promise_resolver.js', '../../../webui/resources/js/util.js', '../../../webui/resources/js/cr/ui.js', '../../../webui/resources/js/cr/event_target.js', diff --git a/chromium/ui/gfx/BUILD.gn b/chromium/ui/gfx/BUILD.gn index f3c6236e5f9..bdff3e39588 100644 --- a/chromium/ui/gfx/BUILD.gn +++ b/chromium/ui/gfx/BUILD.gn @@ -42,6 +42,8 @@ component("gfx") { "animation/animation_container_element.h", "animation/animation_container_observer.h", "animation/animation_delegate.h", + "animation/animation_mac.mm", + "animation/animation_win.cc", "animation/linear_animation.cc", "animation/linear_animation.h", "animation/multi_animation.cc", @@ -53,8 +55,6 @@ component("gfx") { "animation/tween.cc", "animation/tween.h", "break_list.h", - "buffer_format_util.cc", - "buffer_format_util.h", "codec/jpeg_codec.cc", "codec/jpeg_codec.h", "codec/png_codec.cc", @@ -72,6 +72,8 @@ component("gfx") { "display.h", "display_change_notifier.cc", "display_change_notifier.h", + "display_finder.cc", + "display_finder.h", "display_observer.cc", "display_observer.h", "favicon_size.cc", @@ -80,6 +82,7 @@ component("gfx") { "font.h", "font_fallback.h", "font_fallback_linux.cc", + "font_fallback_linux.h", "font_fallback_mac.mm", "font_fallback_win.cc", "font_fallback_win.h", @@ -95,8 +98,6 @@ component("gfx") { "font_render_params_win.cc", "gdi_util.cc", "gdi_util.h", - "generic_shared_memory_id.cc", - "generic_shared_memory_id.h", "gfx_paths.cc", "gfx_paths.h", "icon_util.cc", @@ -113,6 +114,7 @@ component("gfx") { "image/image_skia.h", "image/image_skia_rep.cc", "image/image_skia_rep.h", + "image/image_skia_source.cc", "image/image_skia_source.h", "image/image_skia_util_ios.h", "image/image_skia_util_ios.mm", @@ -131,16 +133,15 @@ component("gfx") { "linux_font_delegate.h", "mac/coordinate_conversion.h", "mac/coordinate_conversion.mm", - "mac/io_surface.cc", - "mac/io_surface.h", "mac/nswindow_frame_controls.h", "mac/nswindow_frame_controls.mm", "mac/scoped_cocoa_disable_screen_updates.h", - "native_widget_types.h", "nine_image_painter.cc", "nine_image_painter.h", "path.cc", "path.h", + "path_mac.h", + "path_mac.mm", "path_win.cc", "path_win.h", "path_x11.cc", @@ -173,8 +174,6 @@ component("gfx") { "screen_aura.cc", "screen_ios.mm", "screen_mac.mm", - "screen_win.cc", - "screen_win.h", "scrollbar_size.cc", "scrollbar_size.h", "selection_model.cc", @@ -214,6 +213,10 @@ component("gfx") { "win/dpi.h", "win/hwnd_util.cc", "win/hwnd_util.h", + "win/physical_size.cc", + "win/physical_size.h", + "win/rendering_window_manager.cc", + "win/rendering_window_manager.h", "win/scoped_set_map_mode.h", "win/singleton_hwnd.cc", "win/singleton_hwnd.h", @@ -233,8 +236,6 @@ component("gfx") { "canvas_paint_mac.mm", "canvas_skia.cc", "canvas_skia_paint.h", - "gpu_memory_buffer.cc", - "gpu_memory_buffer.h", "image/canvas_image_source.cc", "image/canvas_image_source.h", "image/image_skia_operations.cc", @@ -251,8 +252,17 @@ component("gfx") { "//build/config/compiler:no_size_t_to_int_warning", ] + # This is part of the gfx component in the component build. defines = [ "GFX_IMPLEMENTATION" ] + public_deps = [ + ":memory_buffer_sources", + ":native_widget_types", + "//base", + "//skia", + "//third_party/icu", + "//ui/gfx/geometry", + ] deps = [ ":gfx_export", "//base", @@ -263,12 +273,6 @@ component("gfx") { "//third_party/harfbuzz-ng", "//third_party/libpng", "//third_party/zlib", - "//ui/gfx/geometry", - ] - public_deps = [ - "//base", - "//skia", - "//third_party/icu", ] # Text rendering conditions (complicated so separated out). @@ -365,6 +369,8 @@ component("gfx") { if (is_win) { cflags = [ "/wd4324" ] # Structure was padded due to __declspec(align()), which is # uninteresting. + + libs = [ "setupapi.lib" ] } else { sources -= [ "gdi_util.cc", @@ -412,10 +418,6 @@ component("gfx") { sources -= [ "path_x11.cc" ] } - if (use_ozone) { - sources += [ "native_pixmap_handle_ozone.h" ] - } - if (use_cairo) { configs += [ "//build/config/linux:pangocairo" ] } @@ -430,7 +432,6 @@ action("aggregate_vector_icons") { "vector_icons/account_box.icon", "vector_icons/account_child_invert.icon", "vector_icons/apps.icon", - "vector_icons/autofill.icon", "vector_icons/autologin.icon", "vector_icons/bar_close.1x.icon", "vector_icons/bar_close.icon", @@ -488,6 +489,8 @@ action("aggregate_vector_icons") { "vector_icons/location_on.icon", "vector_icons/menu_check.1x.icon", "vector_icons/menu_check.icon", + "vector_icons/menu_radio_empty.icon", + "vector_icons/menu_radio_selected.icon", "vector_icons/midi.icon", "vector_icons/mixed_content.icon", "vector_icons/mode_edit.icon", @@ -529,14 +532,17 @@ action("aggregate_vector_icons") { "vector_icons/tab_audio.icon", "vector_icons/tab_audio_muting.1x.icon", "vector_icons/tab_audio_muting.icon", + "vector_icons/tab_bluetooth_connected.icon", "vector_icons/tab_close_hovered_pressed.1x.icon", "vector_icons/tab_close_hovered_pressed.icon", "vector_icons/tab_close_normal.1x.icon", "vector_icons/tab_close_normal.icon", "vector_icons/tab_media_capturing.icon", "vector_icons/tab_media_recording.icon", + "vector_icons/tab_usb_connected.icon", "vector_icons/tablet.icon", "vector_icons/translate.icon", + "vector_icons/upgrade_menu_item.icon", "vector_icons/warning.icon", "vector_icons/warning_badge.icon", "vector_icons/web.icon", @@ -563,14 +569,86 @@ action("aggregate_vector_icons") { ] args = [ + # TODO(brettw) bug 535386: This should not take a directory as an input, + # but rather a response file listing the inputs or sometimes the build will + # be incorrect. In this case, Ninja won't be able to do proper dependency + # tracking since if a file is deleted, the command line will be the same + # and the action will not be re-run. "--working_directory=" + rebase_path("vector_icons/"), "--output_cc=" + rebase_path(vector_icons_cc_file, root_build_dir), "--output_h=" + rebase_path(vector_icons_h_file, root_build_dir), ] + + if (is_mac) { + sources += [ "vector_icons/combobox_arrow_mac.icon" ] + } } # Looking for gfx_geometry? It's //ui/gfx/geometry:geometry +# Depend on this to use native_widget_types.h without pulling in all of gfx. +source_set("native_widget_types") { + public = [ + "native_widget_types.h", + ] + + public_deps = [ + "//base", + ] +} + +# 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. +# +# The structure here allows the memory buffer to be part of the gfx component +# in the component build, but be a separate source set in a static build. +group("memory_buffer") { + if (is_component_build) { + public_deps = [ + ":gfx", + ] + } else { + public_deps = [ + ":memory_buffer_sources", + ] + } +} +source_set("memory_buffer_sources") { + visibility = [ ":*" ] # Depend on through ":memory_buffer". + + # TODO(brettw) refactor this so these sources are in a coherent directory + # structure rather than random samplings of ui/gfx and ui/gfx/mac. + sources = [ + "buffer_format_util.cc", + "buffer_format_util.h", + "buffer_types.h", + "generic_shared_memory_id.cc", + "generic_shared_memory_id.h", + "gfx_export.h", + "mac/io_surface.cc", + "mac/io_surface.h", + ] + + if (!is_ios) { + sources += [ + "gpu_memory_buffer.cc", + "gpu_memory_buffer.h", + ] + } + + if (use_ozone) { + sources += [ "native_pixmap_handle_ozone.h" ] + } + + defines = [ "GFX_IMPLEMENTATION" ] + + deps = [ + "//base", + "//ui/gfx/geometry", + ] +} + source_set("test_support") { testonly = true sources = [ @@ -578,6 +656,7 @@ source_set("test_support") { "image/image_unittest_util.h", "image/image_unittest_util_ios.mm", "image/image_unittest_util_mac.mm", + "test/display_util.h", "test/fontconfig_util_linux.cc", "test/fontconfig_util_linux.h", "test/gfx_util.cc", @@ -612,12 +691,14 @@ source_set("test_support") { } } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("gfx_unittests_run") { +bundle_data("gfx_unittests_bundle_data") { testonly = true - deps = [ - ":gfx_unittests", + sources = [ + "test/data", + ] + outputs = [ + "{{bundle_resources_dir}}/" + + "{{source_root_relative_dir}}/{{source_file_part}}", ] } @@ -680,6 +761,7 @@ test("gfx_unittests") { "image/image_util_unittest.cc", "mac/coordinate_conversion_unittest.mm", "nine_image_painter_unittest.cc", + "path_mac_unittest.mm", "platform_font_mac_unittest.mm", "range/range_mac_unittest.mm", "range/range_unittest.cc", @@ -702,6 +784,7 @@ test("gfx_unittests") { deps = [ ":gfx", + ":gfx_unittests_bundle_data", ":test_support", "//base", "//base/test:test_support", diff --git a/chromium/ui/gfx/animation/animation.cc b/chromium/ui/gfx/animation/animation.cc index 3c298285e13..4bc1b248aa7 100644 --- a/chromium/ui/gfx/animation/animation.cc +++ b/chromium/ui/gfx/animation/animation.cc @@ -10,10 +10,6 @@ #include "ui/gfx/animation/tween.h" #include "ui/gfx/geometry/rect.h" -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif - namespace gfx { Animation::Animation(base::TimeDelta timer_interval) @@ -92,21 +88,21 @@ void Animation::SetContainer(AnimationContainer* container) { container_->Start(this); } +#if !defined(OS_WIN) // static bool Animation::ShouldRenderRichAnimation() { -#if defined(OS_WIN) - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - BOOL result; - // Get "Turn off all unnecessary animations" value. - if (::SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &result, 0)) { - return !!result; - } - } - return !::GetSystemMetrics(SM_REMOTESESSION); -#else + // Defined in platform specific file for Windows. return true; +} #endif + +#if !defined(OS_WIN) && !defined(OS_MACOSX) +// static +bool Animation::ScrollAnimationsEnabledBySystem() { + // Defined in platform specific files for Windows and OSX. + return true; } +#endif bool Animation::ShouldSendCanceledFromStop() { return false; diff --git a/chromium/ui/gfx/animation/animation.h b/chromium/ui/gfx/animation/animation.h index 44823b0366f..8d8b4d0335b 100644 --- a/chromium/ui/gfx/animation/animation.h +++ b/chromium/ui/gfx/animation/animation.h @@ -64,6 +64,11 @@ class GFX_EXPORT Animation : public AnimationContainerElement { // to give guidance for heavy animations such as "start download" arrow. static bool ShouldRenderRichAnimation(); + // Determines on a per-platform basis whether scroll animations (e.g. produced + // by home/end key) should be enabled. Should only be called from the browser + // process. + static bool ScrollAnimationsEnabledBySystem(); + protected: // Invoked from Start to allow subclasses to prepare for the animation. virtual void AnimationStarted() {} diff --git a/chromium/ui/gfx/animation/animation_mac.mm b/chromium/ui/gfx/animation/animation_mac.mm new file mode 100644 index 00000000000..e3a4ff2ab0f --- /dev/null +++ b/chromium/ui/gfx/animation/animation_mac.mm @@ -0,0 +1,33 @@ +// 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/animation/animation.h" + +#include "base/mac/mac_util.h" +#include "base/message_loop/message_loop.h" + +namespace gfx { + +// static +bool Animation::ScrollAnimationsEnabledBySystem() { + // Because of sandboxing, OS settings should only be queried from the browser + // process. + DCHECK(base::MessageLoopForUI::IsCurrent() || + base::MessageLoopForIO::IsCurrent()); + + bool enabled = false; + id value = nil; + if (base::mac::IsOSMountainLionOrLater()) { + value = [[NSUserDefaults standardUserDefaults] + objectForKey:@"NSScrollAnimationEnabled"]; + } else { + value = [[NSUserDefaults standardUserDefaults] + objectForKey:@"AppleScrollAnimationEnabled"]; + } + if (value) + enabled = [value boolValue]; + return enabled; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/animation/animation_win.cc b/chromium/ui/gfx/animation/animation_win.cc new file mode 100644 index 00000000000..b8a7d2880a7 --- /dev/null +++ b/chromium/ui/gfx/animation/animation_win.cc @@ -0,0 +1,28 @@ +// 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/animation/animation.h" + +#include "base/win/windows_version.h" + +namespace gfx { + +// static +bool Animation::ShouldRenderRichAnimation() { + if (base::win::GetVersion() >= base::win::VERSION_VISTA) { + BOOL result; + // Get "Turn off all unnecessary animations" value. + if (::SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &result, 0)) { + return !!result; + } + } + return !::GetSystemMetrics(SM_REMOTESESSION); +} + +// static +bool Animation::ScrollAnimationsEnabledBySystem() { + return ShouldRenderRichAnimation(); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/animation/multi_animation.h b/chromium/ui/gfx/animation/multi_animation.h index fd28e386493..a74656989ea 100644 --- a/chromium/ui/gfx/animation/multi_animation.h +++ b/chromium/ui/gfx/animation/multi_animation.h @@ -33,11 +33,12 @@ class GFX_EXPORT MultiAnimation : public Animation { // for 200ms with a % between .25 and .75 use the following three values: 200, // 100, 400. struct Part { - Part() : time_ms(0), start_time_ms(0), end_time_ms(0), type(Tween::ZERO) {} - Part(int time_ms, Tween::Type type) + Part() : Part(0, Tween::ZERO) {} + Part(int time_ms, Tween::Type type) : Part(time_ms, 0, time_ms, type) {} + Part(int time_ms, int start_time_ms, int end_time_ms, Tween::Type type) : time_ms(time_ms), - start_time_ms(0), - end_time_ms(time_ms), + start_time_ms(start_time_ms), + end_time_ms(end_time_ms), type(type) {} int time_ms; diff --git a/chromium/ui/gfx/animation/multi_animation_unittest.cc b/chromium/ui/gfx/animation/multi_animation_unittest.cc index b8a11fdacf2..f8bff9a0f0b 100644 --- a/chromium/ui/gfx/animation/multi_animation_unittest.cc +++ b/chromium/ui/gfx/animation/multi_animation_unittest.cc @@ -40,9 +40,7 @@ TEST(MultiAnimationTest, Basic) { TEST(MultiAnimationTest, DifferingStartAndEnd) { // Create a MultiAnimation with two parts. MultiAnimation::Parts parts; - parts.push_back(MultiAnimation::Part(200, Tween::LINEAR)); - parts[0].start_time_ms = 100; - parts[0].end_time_ms = 400; + parts.push_back(MultiAnimation::Part(200, 100, 400, Tween::LINEAR)); MultiAnimation animation(parts, MultiAnimation::GetDefaultTimerInterval()); AnimationContainerElement* as_element = diff --git a/chromium/ui/gfx/blit_unittest.cc b/chromium/ui/gfx/blit_unittest.cc index e6245916f76..7598ef65828 100644 --- a/chromium/ui/gfx/blit_unittest.cc +++ b/chromium/ui/gfx/blit_unittest.cc @@ -21,7 +21,7 @@ namespace { // 0x00000000 0x01010101 // 0x12121212 0xFFFFFFFF template <int w, int h> -void SetToCanvas(skia::PlatformCanvas* canvas, uint8_t values[h][w]) { +void SetToCanvas(SkCanvas* canvas, uint8_t values[h][w]) { ASSERT_EQ(w, canvas->imageInfo().width()); ASSERT_EQ(h, canvas->imageInfo().height()); @@ -44,7 +44,7 @@ void SetToCanvas(skia::PlatformCanvas* canvas, uint8_t values[h][w]) { // values, where each value has been duplicated into each channel of the given // bitmap (see SetToCanvas above). template <int w, int h> -void VerifyCanvasValues(skia::PlatformCanvas* canvas, uint8_t values[h][w]) { +void VerifyCanvasValues(SkCanvas* canvas, uint8_t values[h][w]) { SkBitmap bitmap = skia::ReadPixels(canvas); SkAutoLockPixels lock(bitmap); ASSERT_EQ(w, bitmap.width()); @@ -64,7 +64,7 @@ void VerifyCanvasValues(skia::PlatformCanvas* canvas, uint8_t values[h][w]) { TEST(Blit, ScrollCanvas) { static const int kCanvasWidth = 5; static const int kCanvasHeight = 5; - skia::RefPtr<SkCanvas> canvas = skia::AdoptRef( + sk_sp<SkCanvas> canvas( skia::CreatePlatformCanvas(kCanvasWidth, kCanvasHeight, true)); uint8_t initial_values[kCanvasHeight][kCanvasWidth] = { {0x00, 0x01, 0x02, 0x03, 0x04}, @@ -152,7 +152,7 @@ TEST(Blit, WithSharedMemory) { base::SharedMemory shared_mem; ASSERT_TRUE(shared_mem.CreateAnonymous(kCanvasWidth * kCanvasHeight)); base::SharedMemoryHandle section = shared_mem.handle(); - skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(skia::CreatePlatformCanvas( + sk_sp<SkCanvas> canvas(skia::CreatePlatformCanvas( kCanvasWidth, kCanvasHeight, true, section.GetHandle(), skia::RETURN_NULL_ON_FAILURE)); ASSERT_TRUE(canvas); diff --git a/chromium/ui/gfx/canvas.cc b/chromium/ui/gfx/canvas.cc index 459a0e69b59..b88fd2d5512 100644 --- a/chromium/ui/gfx/canvas.cc +++ b/chromium/ui/gfx/canvas.cc @@ -10,6 +10,7 @@ #include "base/i18n/rtl.h" #include "base/logging.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "ui/gfx/font_list.h" #include "ui/gfx/geometry/rect.h" @@ -24,18 +25,16 @@ namespace gfx { Canvas::Canvas(const Size& size, float image_scale, bool is_opaque) - : image_scale_(image_scale), - canvas_(NULL) { + : image_scale_(image_scale) { Size pixel_size = ScaleToCeiledSize(size, image_scale); - owned_canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), - pixel_size.height(), - is_opaque)); - canvas_ = owned_canvas_.get(); + canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), + pixel_size.height(), + is_opaque)); #if !defined(USE_CAIRO) // skia::PlatformCanvas instances are initialized to 0 by Cairo, but // uninitialized on other platforms. if (!is_opaque) - owned_canvas_->clear(SkColorSetARGB(0, 0, 0, 0)); + canvas_->clear(SkColorSetARGB(0, 0, 0, 0)); #endif SkScalar scale_scalar = SkFloatToScalar(image_scale); @@ -44,11 +43,10 @@ Canvas::Canvas(const Size& size, float image_scale, bool is_opaque) Canvas::Canvas(const ImageSkiaRep& image_rep, bool is_opaque) : image_scale_(image_rep.scale()), - owned_canvas_(skia::AdoptRef( + canvas_(skia::AdoptRef( skia::CreatePlatformCanvas(image_rep.pixel_width(), image_rep.pixel_height(), - is_opaque))), - canvas_(owned_canvas_.get()) { + is_opaque))) { SkScalar scale_scalar = SkFloatToScalar(image_scale_); canvas_->scale(scale_scalar, scale_scalar); DrawImageInt(ImageSkia(image_rep), 0, 0); @@ -56,11 +54,10 @@ Canvas::Canvas(const ImageSkiaRep& image_rep, bool is_opaque) Canvas::Canvas() : image_scale_(1.f), - owned_canvas_(skia::AdoptRef(skia::CreatePlatformCanvas(0, 0, false))), - canvas_(owned_canvas_.get()) {} + canvas_(skia::AdoptRef(skia::CreatePlatformCanvas(0, 0, false))) {} -Canvas::Canvas(SkCanvas* canvas, float image_scale) - : image_scale_(image_scale), owned_canvas_(), canvas_(canvas) { +Canvas::Canvas(const skia::RefPtr<SkCanvas>& canvas, float image_scale) + : image_scale_(image_scale), canvas_(canvas) { DCHECK(canvas); } @@ -72,10 +69,9 @@ void Canvas::RecreateBackingCanvas(const Size& size, bool is_opaque) { image_scale_ = image_scale; Size pixel_size = ScaleToFlooredSize(size, image_scale); - owned_canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), - pixel_size.height(), - is_opaque)); - canvas_ = owned_canvas_.get(); + canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), + pixel_size.height(), + is_opaque)); SkScalar scale_scalar = SkFloatToScalar(image_scale); canvas_->scale(scale_scalar, scale_scalar); } @@ -157,16 +153,10 @@ void Canvas::DrawDashedRect(const Rect& rect, SkColor color) { } } - // Make a shader for the bitmap with an origin of the box we'll draw. This - // shader is refcounted and will have an initial refcount of 1. - skia::RefPtr<SkShader> shader = skia::AdoptRef( - SkShader::CreateBitmapShader( - *dots, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); - // Assign the shader to the paint & release our reference. The paint will - // now own the shader and the shader will be destroyed when the paint goes - // out of scope. + // Make a shader for the bitmap with an origin of the box we'll draw. SkPaint paint; - paint.setShader(shader.get()); + paint.setShader(SkShader::MakeBitmapShader(*dots, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode)); DrawRect(Rect(rect.x(), rect.y(), rect.width(), 1), paint); DrawRect(Rect(rect.x(), rect.y() + rect.height() - 1, rect.width(), 1), @@ -428,13 +418,10 @@ void Canvas::DrawImageInPath(const ImageSkia& image, SkMatrix matrix; matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); - skia::RefPtr<SkShader> shader = CreateImageRepShader( - image_rep, - SkShader::kRepeat_TileMode, - matrix); - SkPaint p(paint); - p.setShader(shader.get()); + p.setShader(CreateImageRepShader(image_rep, + SkShader::kRepeat_TileMode, + matrix)); canvas_->drawPath(path, p); } @@ -482,12 +469,30 @@ void Canvas::TileImageInt(const ImageSkia& image, int dest_y, int w, int h) { - if (!IntersectsClipRectInt(dest_x, dest_y, w, h)) + SkRect dest_rect = { SkIntToScalar(dest_x), + SkIntToScalar(dest_y), + SkIntToScalar(dest_x + w), + SkIntToScalar(dest_y + h) }; + if (!IntersectsClipRect(dest_rect)) return; + SkPaint paint; + if (InitSkPaintForTiling(image, src_x, src_y, tile_scale_x, tile_scale_y, + dest_x, dest_y, &paint)) + canvas_->drawRect(dest_rect, paint); +} + +bool Canvas::InitSkPaintForTiling(const ImageSkia& image, + int src_x, + int src_y, + float tile_scale_x, + float tile_scale_y, + int dest_x, + int dest_y, + SkPaint* paint) { const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); if (image_rep.is_null()) - return; + return false; SkMatrix shader_scale; shader_scale.setScale(SkFloatToScalar(tile_scale_x), @@ -495,36 +500,19 @@ void Canvas::TileImageInt(const ImageSkia& image, shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); - skia::RefPtr<SkShader> shader = CreateImageRepShader( - image_rep, - SkShader::kRepeat_TileMode, - shader_scale); - - SkPaint paint; - paint.setShader(shader.get()); - paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); - - SkRect dest_rect = { SkIntToScalar(dest_x), - SkIntToScalar(dest_y), - SkIntToScalar(dest_x + w), - SkIntToScalar(dest_y + h) }; - canvas_->drawRect(dest_rect, paint); + paint->setShader(CreateImageRepShader(image_rep, SkShader::kRepeat_TileMode, + shader_scale)); + paint->setXfermodeMode(SkXfermode::kSrcOver_Mode); + return true; } void Canvas::Transform(const gfx::Transform& transform) { canvas_->concat(transform.matrix()); } -bool Canvas::IntersectsClipRectInt(int x, int y, int w, int h) { +bool Canvas::IntersectsClipRect(const SkRect& rect) { SkRect clip; - return canvas_->getClipBounds(&clip) && - clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w), - SkIntToScalar(y + h)); -} - -bool Canvas::IntersectsClipRect(const Rect& rect) { - return IntersectsClipRectInt(rect.x(), rect.y(), - rect.width(), rect.height()); + return canvas_->getClipBounds(&clip) && clip.intersects(rect); } void Canvas::DrawImageIntHelper(const ImageSkiaRep& image_rep, @@ -546,16 +534,15 @@ void Canvas::DrawImageIntHelper(const ImageSkiaRep& image_rep, return; } - if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h)) - return; - - float user_scale_x = static_cast<float>(dest_w) / src_w; - float user_scale_y = static_cast<float>(dest_h) / src_h; - SkRect dest_rect = { SkIntToScalar(dest_x), SkIntToScalar(dest_y), SkIntToScalar(dest_x + dest_w), SkIntToScalar(dest_y + dest_h) }; + if (!IntersectsClipRect(dest_rect)) + return; + + float user_scale_x = static_cast<float>(dest_w) / src_w; + float user_scale_y = static_cast<float>(dest_h) / src_h; // Make a bitmap shader that contains the bitmap we want to draw. This is // basically what SkCanvas.drawBitmap does internally, but it gives us @@ -567,15 +554,11 @@ void Canvas::DrawImageIntHelper(const ImageSkiaRep& image_rep, shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); - skia::RefPtr<SkShader> shader = CreateImageRepShaderForScale( - image_rep, SkShader::kRepeat_TileMode, shader_scale, - remove_image_scale ? image_rep.scale() : 1.f); - - // Set up our paint to use the shader & release our reference (now just owned - // by the paint). SkPaint p(paint); p.setFilterQuality(filter ? kLow_SkFilterQuality : kNone_SkFilterQuality); - p.setShader(shader.get()); + p.setShader(CreateImageRepShaderForScale( + image_rep, SkShader::kRepeat_TileMode, shader_scale, + remove_image_scale ? image_rep.scale() : 1.f)); // The rect will be filled by the bitmap. canvas_->drawRect(dest_rect, p); diff --git a/chromium/ui/gfx/canvas.h b/chromium/ui/gfx/canvas.h index 311cf38830c..367aa8a7441 100644 --- a/chromium/ui/gfx/canvas.h +++ b/chromium/ui/gfx/canvas.h @@ -89,7 +89,7 @@ class GFX_EXPORT Canvas { // Creates a Canvas backed by an |sk_canvas| with |image_scale_|. // |sk_canvas| is assumed to be already scaled based on |image_scale| // so no additional scaling is applied. - Canvas(SkCanvas* sk_canvas, float image_scale); + Canvas(const skia::RefPtr<SkCanvas>& sk_canvas, float image_scale); virtual ~Canvas(); @@ -399,6 +399,18 @@ class GFX_EXPORT Canvas { int w, int h); + // Helper for TileImageInt(). Initializes |paint| for tiling |image| with the + // given parameters. Returns false if the provided image does not have a + // representation for the current scale. + bool InitSkPaintForTiling(const ImageSkia& image, + int src_x, + int src_y, + float tile_scale_x, + float tile_scale_y, + int dest_x, + int dest_y, + SkPaint* paint); + // Apply transformation on the canvas. void Transform(const Transform& transform); @@ -409,14 +421,12 @@ class GFX_EXPORT Canvas { const Rect& display_rect, int flags); - skia::PlatformCanvas* platform_canvas() { return owned_canvas_.get(); } - SkCanvas* sk_canvas() { return canvas_; } + SkCanvas* sk_canvas() { return canvas_.get(); } float image_scale() const { return image_scale_; } private: - // Test whether the provided rectangle intersects the current clip rect. - bool IntersectsClipRectInt(int x, int y, int w, int h); - bool IntersectsClipRect(const Rect& rect); + // Tests whether the provided rectangle intersects the current clip rect. + bool IntersectsClipRect(const SkRect& rect); // Helper for the DrawImageInt functions declared above. The // |remove_image_scale| parameter indicates if the scale of the |image_rep| @@ -439,8 +449,7 @@ class GFX_EXPORT Canvas { // Canvas::Scale() does not affect |image_scale_|. float image_scale_; - skia::RefPtr<skia::PlatformCanvas> owned_canvas_; - SkCanvas* canvas_; + skia::RefPtr<SkCanvas> canvas_; DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/chromium/ui/gfx/canvas_paint_mac.h b/chromium/ui/gfx/canvas_paint_mac.h index b8d9974db9f..1623b4baed9 100644 --- a/chromium/ui/gfx/canvas_paint_mac.h +++ b/chromium/ui/gfx/canvas_paint_mac.h @@ -43,7 +43,6 @@ class GFX_EXPORT CanvasSkiaPaint : public Canvas { private: void Init(bool opaque); - CGContext* context_; NSRect rectangle_; // See description above setter. bool composite_alpha_; diff --git a/chromium/ui/gfx/canvas_paint_mac.mm b/chromium/ui/gfx/canvas_paint_mac.mm index cc1c6a4f744..29fdfe5ecf8 100644 --- a/chromium/ui/gfx/canvas_paint_mac.mm +++ b/chromium/ui/gfx/canvas_paint_mac.mm @@ -8,25 +8,25 @@ namespace gfx { CanvasSkiaPaint::CanvasSkiaPaint(NSRect dirtyRect) - : context_(NULL), - rectangle_(dirtyRect), + : rectangle_(dirtyRect), composite_alpha_(false) { Init(true); } CanvasSkiaPaint::CanvasSkiaPaint(NSRect dirtyRect, bool opaque) - : context_(NULL), - rectangle_(dirtyRect), + : rectangle_(dirtyRect), composite_alpha_(false) { Init(opaque); } CanvasSkiaPaint::~CanvasSkiaPaint() { if (!is_empty()) { - platform_canvas()->restoreToCount(1); + SkCanvas* canvas = sk_canvas(); + canvas->restoreToCount(1); // Blit the dirty rect to the current context. - CGImageRef image = CGBitmapContextCreateImage(context_); + CGImageRef image = + CGBitmapContextCreateImage(skia::GetBitmapContext(*canvas)); CGRect dest_rect = NSRectToCGRect(rectangle_); CGContextRef destination_context = @@ -60,15 +60,13 @@ void CanvasSkiaPaint::Init(bool opaque) { gfx::Size size(NSWidth(rectangle_), NSHeight(rectangle_)); RecreateBackingCanvas(size, scale, opaque); - skia::PlatformCanvas* canvas = platform_canvas(); + SkCanvas* canvas = sk_canvas(); canvas->clear(SkColorSetARGB(0, 0, 0, 0)); // Need to translate so that the dirty region appears at the origin of the // surface. canvas->translate(-SkDoubleToScalar(NSMinX(rectangle_)), -SkDoubleToScalar(NSMinY(rectangle_))); - - context_ = skia::GetBitmapContext(*canvas); } } // namespace skia diff --git a/chromium/ui/gfx/canvas_skia.cc b/chromium/ui/gfx/canvas_skia.cc index 8f45fce059c..d20f92fe56d 100644 --- a/chromium/ui/gfx/canvas_skia.cc +++ b/chromium/ui/gfx/canvas_skia.cc @@ -21,6 +21,7 @@ #include "ui/gfx/range/range.h" #include "ui/gfx/render_text.h" #include "ui/gfx/shadow_value.h" +#include "ui/gfx/skia_util.h" #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" @@ -187,7 +188,7 @@ void Canvas::DrawStringRectWithShadows(const base::string16& text, int line_height, int flags, const ShadowValues& shadows) { - if (!IntersectsClipRect(text_bounds)) + if (!IntersectsClipRect(RectToSkRect(text_bounds))) return; Rect clip_rect(text_bounds); diff --git a/chromium/ui/gfx/color_palette.h b/chromium/ui/gfx/color_palette.h index f39e4437922..cb7271ac5aa 100644 --- a/chromium/ui/gfx/color_palette.h +++ b/chromium/ui/gfx/color_palette.h @@ -27,6 +27,15 @@ const SkColor kGoogleGreen700 = SkColorSetRGB(0x0B, 0x80, 0x43); const SkColor kGoogleYellow300 = SkColorSetRGB(0xF7, 0xCB, 0x4D); const SkColor kGoogleYellow700 = SkColorSetRGB(0xF0, 0x93, 0x00); +// Material Design canonical colors, from +// https://www.google.com/design/spec/style/color.html#color-color-palette +const SkColor kMaterialBlue300 = SkColorSetRGB(0x64, 0xB5, 0xF6); +const SkColor kMaterialBlue500 = SkColorSetRGB(0x21, 0x96, 0xF3); +const SkColor kMaterialBlue700 = SkColorSetRGB(0x19, 0x76, 0xD2); + +const SkColor kMaterialGrey300 = SkColorSetRGB(0xE0, 0xE0, 0xE0); +const SkColor kMaterialGrey500 = SkColorSetRGB(0x9E, 0x9E, 0x9E); + } // namespace gfx #endif // UI_GFX_COLOR_PALETTE_H_ diff --git a/chromium/ui/gfx/color_utils.cc b/chromium/ui/gfx/color_utils.cc index f30c266e1be..a4618f27e66 100644 --- a/chromium/ui/gfx/color_utils.cc +++ b/chromium/ui/gfx/color_utils.cc @@ -14,6 +14,7 @@ #include "build/build_config.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/color_palette.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" #if defined(OS_WIN) #include <windows.h> @@ -41,35 +42,21 @@ int calcHue(double temp1, double temp2, double hue) { else if (hue * 3.0 < 2.0) result = temp1 + (temp2 - temp1) * (2.0 / 3.0 - hue) * 6.0; - // Scale the result from 0 - 255 and round off the value. - return static_cast<int>(result * 255 + .5); + return static_cast<int>(std::round(result * 255)); } -// Next two functions' formulas from: -// http://www.w3.org/TR/WCAG20/#relativeluminancedef -// http://www.w3.org/TR/WCAG20/#contrast-ratiodef - -double ConvertSRGB(double eight_bit_component) { +// Assumes sRGB. +double Linearize(double eight_bit_component) { const double component = eight_bit_component / 255.0; return (component <= 0.03928) ? (component / 12.92) : pow((component + 0.055) / 1.055, 2.4); } -SkColor LumaInvertColor(SkColor color) { +SkColor LightnessInvertColor(SkColor color) { HSL hsl; SkColorToHSL(color, &hsl); hsl.l = 1.0 - hsl.l; - return HSLToSkColor(hsl, 255); -} - -double ContrastRatio(double foreground_luminance, double background_luminance) { - DCHECK_GE(foreground_luminance, 0.0); - DCHECK_GE(background_luminance, 0.0); - foreground_luminance += 0.05; - background_luminance += 0.05; - return (foreground_luminance > background_luminance) ? - (foreground_luminance / background_luminance) : - (background_luminance / foreground_luminance); + return HSLToSkColor(hsl, SkColorGetA(color)); } } // namespace @@ -78,20 +65,29 @@ double ContrastRatio(double foreground_luminance, double background_luminance) { // ---------------------------------------------------------------------------- double GetContrastRatio(SkColor color_a, SkColor color_b) { - return ContrastRatio(RelativeLuminance(color_a), RelativeLuminance(color_b)); + return GetContrastRatio(GetRelativeLuminance(color_a), + GetRelativeLuminance(color_b)); } -unsigned char GetLuminanceForColor(SkColor color) { - return base::saturated_cast<unsigned char>( - (0.3 * SkColorGetR(color)) + - (0.59 * SkColorGetG(color)) + - (0.11 * SkColorGetB(color))); +double GetContrastRatio(double luminance_a, double luminance_b) { + DCHECK_GE(luminance_a, 0.0); + DCHECK_GE(luminance_b, 0.0); + luminance_a += 0.05; + luminance_b += 0.05; + return (luminance_a > luminance_b) ? (luminance_a / luminance_b) + : (luminance_b / luminance_a); } -double RelativeLuminance(SkColor color) { - return (0.2126 * ConvertSRGB(SkColorGetR(color))) + - (0.7152 * ConvertSRGB(SkColorGetG(color))) + - (0.0722 * ConvertSRGB(SkColorGetB(color))); +double GetRelativeLuminance(SkColor color) { + return (0.2126 * Linearize(SkColorGetR(color))) + + (0.7152 * Linearize(SkColorGetG(color))) + + (0.0722 * Linearize(SkColorGetB(color))); +} + +uint8_t GetLuma(SkColor color) { + return static_cast<uint8_t>(std::round((0.299 * SkColorGetR(color)) + + (0.587 * SkColorGetG(color)) + + (0.114 * SkColorGetB(color)))); } void SkColorToHSL(SkColor c, HSL* hsl) { @@ -134,15 +130,8 @@ SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) { // If there's no color, we don't care about hue and can do everything based on // brightness. if (!saturation) { - uint8_t light; - - if (lightness < 0) - light = 0; - else if (lightness >= 1.0) - light = 255; - else - light = static_cast<uint8_t>(SkDoubleToFixed(lightness) >> 8); - + const uint8_t light = + base::saturated_cast<uint8_t>(gfx::ToRoundedInt(lightness * 255)); return SkColorSetARGB(alpha, light, light, light); } @@ -150,10 +139,9 @@ SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) { (lightness * (1.0 + saturation)) : (lightness + saturation - (lightness * saturation)); double temp1 = 2.0 * lightness - temp2; - return SkColorSetARGB(alpha, - calcHue(temp1, temp2, hue + 1.0 / 3.0), - calcHue(temp1, temp2, hue), - calcHue(temp1, temp2, hue - 1.0 / 3.0)); + return SkColorSetARGB(alpha, calcHue(temp1, temp2, hue + 1.0 / 3.0), + calcHue(temp1, temp2, hue), + calcHue(temp1, temp2, hue - 1.0 / 3.0)); } bool IsWithinHSLRange(const HSL& hsl, @@ -249,7 +237,7 @@ void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) { int pixel_height = bitmap.height(); for (int y = 0; y < pixel_height; ++y) { for (int x = 0; x < pixel_width; ++x) - ++histogram[GetLuminanceForColor(bitmap.getColor(x, y))]; + ++histogram[GetLuma(bitmap.getColor(x, y))]; } } @@ -287,35 +275,40 @@ SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha) { double b = (SkColorGetB(foreground) * f_weight + SkColorGetB(background) * b_weight) / 255.0; - return SkColorSetARGB(static_cast<int>(normalizer), - static_cast<int>(r), - static_cast<int>(g), - static_cast<int>(b)); + return SkColorSetARGB(static_cast<int>(std::round(normalizer)), + static_cast<int>(std::round(r)), + static_cast<int>(std::round(g)), + static_cast<int>(std::round(b))); } bool IsDark(SkColor color) { - return GetLuminanceForColor(color) < 128; + return GetLuma(color) < 128; } -SkColor BlendTowardOppositeLuminance(SkColor color, SkAlpha alpha) { +SkColor BlendTowardOppositeLuma(SkColor color, SkAlpha alpha) { return AlphaBlend(IsDark(color) ? SK_ColorWHITE : SK_ColorBLACK, color, alpha); } SkColor GetReadableColor(SkColor foreground, SkColor background) { - const SkColor foreground2 = LumaInvertColor(foreground); - const double background_luminance = RelativeLuminance(background); - return (ContrastRatio(RelativeLuminance(foreground), background_luminance) >= - ContrastRatio(RelativeLuminance(foreground2), background_luminance)) ? - foreground : foreground2; + return PickContrastingColor(foreground, LightnessInvertColor(foreground), + background); +} + +SkColor PickContrastingColor(SkColor foreground1, + SkColor foreground2, + SkColor background) { + const double background_luminance = GetRelativeLuminance(background); + return (GetContrastRatio(GetRelativeLuminance(foreground1), + background_luminance) >= + GetContrastRatio(GetRelativeLuminance(foreground2), + background_luminance)) ? + foreground1 : foreground2; } SkColor InvertColor(SkColor color) { - return SkColorSetARGB( - SkColorGetA(color), - 255 - SkColorGetR(color), - 255 - SkColorGetG(color), - 255 - SkColorGetB(color)); + return SkColorSetARGB(SkColorGetA(color), 255 - SkColorGetR(color), + 255 - SkColorGetG(color), 255 - SkColorGetB(color)); } SkColor GetSysSkColor(int which) { diff --git a/chromium/ui/gfx/color_utils.h b/chromium/ui/gfx/color_utils.h index 44302c808c8..fc8b4465286 100644 --- a/chromium/ui/gfx/color_utils.h +++ b/chromium/ui/gfx/color_utils.h @@ -23,13 +23,21 @@ struct HSL { // This value is taken from w3c accessibility guidelines. const double kMinimumReadableContrastRatio = 4.5f; -// Determines the contrast ratio of two colors. +// Determines the contrast ratio of two colors or two relative luminance values +// (as computed by RelativeLuminance()), calculated according to +// http://www.w3.org/TR/WCAG20/#contrast-ratiodef . GFX_EXPORT double GetContrastRatio(SkColor color_a, SkColor color_b); +GFX_EXPORT double GetContrastRatio(double luminance_a, double luminance_b); -GFX_EXPORT unsigned char GetLuminanceForColor(SkColor color); +// The relative luminance of |color|, that is, the weighted sum of the +// linearized RGB components, normalized to 0..1, per BT.709. See +// http://www.w3.org/TR/WCAG20/#relativeluminancedef . +GFX_EXPORT double GetRelativeLuminance(SkColor color); -// Calculated according to http://www.w3.org/TR/WCAG20/#relativeluminancedef -GFX_EXPORT double RelativeLuminance(SkColor color); +// The luma of |color|, that is, the weighted sum of the gamma-compressed R'G'B' +// components, per BT.601, a.k.a. the Y' in Y'UV. See +// https://en.wikipedia.org/wiki/Luma_(video). +GFX_EXPORT uint8_t GetLuma(SkColor color); // Note: these transformations assume sRGB as the source color space GFX_EXPORT void SkColorToHSL(SkColor c, HSL* hsl); @@ -50,8 +58,8 @@ GFX_EXPORT bool IsWithinHSLRange(const HSL& hsl, const HSL& upper_bound); // Makes |hsl| valid input for HSLShift(). Sets values of hue, saturation -// and luminosity which are outside of the valid range [0, 1] to -1. -// -1 is a special value which indicates 'no change'. +// and lightness which are outside of the valid range [0, 1] to -1. -1 is a +// special value which indicates 'no change'. GFX_EXPORT void MakeHSLShiftValid(HSL* hsl); // HSL-Shift an SkColor. The shift values are in the range of 0-1, with the @@ -70,8 +78,7 @@ GFX_EXPORT void MakeHSLShiftValid(HSL* hsl); // 1 = full lightness (make all pixels white). GFX_EXPORT SkColor HSLShift(SkColor color, const HSL& shift); -// Builds a histogram based on the Y' of the Y'UV representation of -// this image. +// Builds a histogram based on the Y' of the Y'UV representation of this image. GFX_EXPORT void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]); // Calculates how "boring" an image is. The boring score is the @@ -87,25 +94,30 @@ GFX_EXPORT double CalculateBoringScore(const SkBitmap& bitmap); GFX_EXPORT SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha); -// Returns true if the luminance of |color| is closer to black than white. +// Returns true if the luma of |color| is closer to black than white. GFX_EXPORT bool IsDark(SkColor color); // Makes a dark color lighter or a light color darker by blending |color| with -// white or black depending on its current luminance. |alpha| controls the -// amount of white or black that will be alpha-blended into |color|. -GFX_EXPORT SkColor BlendTowardOppositeLuminance(SkColor color, SkAlpha alpha); - -// Given an opaque foreground and background color, try to return a foreground -// color that is "readable" over the background color by luma-inverting the -// foreground color and then picking whichever foreground color has higher -// contrast against the background color. You should not pass colors with -// non-255 alpha to this routine, since determining the correct behavior in such -// cases can be impossible. +// white or black depending on its current luma. |alpha| controls the amount of +// white or black that will be alpha-blended into |color|. +GFX_EXPORT SkColor BlendTowardOppositeLuma(SkColor color, SkAlpha alpha); + +// Given a foreground and background color, try to return a foreground color +// that is "readable" over the background color by luma-inverting the foreground +// color and then using PickContrastingColor() to pick the one with greater +// contrast. During this process, alpha values will be ignored; the returned +// color will have the same alpha as |foreground|. // // NOTE: This won't do anything but waste time if the supplied foreground color // has a luma value close to the midpoint (0.5 in the HSL representation). GFX_EXPORT SkColor GetReadableColor(SkColor foreground, SkColor background); +// Returns whichever of |foreground1| or |foreground2| has higher contrast with +// |background|. +GFX_EXPORT SkColor PickContrastingColor(SkColor foreground1, + SkColor foreground2, + SkColor background); + // Invert a color. GFX_EXPORT SkColor InvertColor(SkColor color); diff --git a/chromium/ui/gfx/display.cc b/chromium/ui/gfx/display.cc index 1777e87e543..7a9539d9bf4 100644 --- a/chromium/ui/gfx/display.cc +++ b/chromium/ui/gfx/display.cc @@ -80,6 +80,8 @@ Display::Display() touch_support_(TOUCH_SUPPORT_UNKNOWN) { } +Display::Display(const Display& other) = default; + Display::Display(int64_t id) : id_(id), device_scale_factor_(GetForcedDeviceScaleFactor()), diff --git a/chromium/ui/gfx/display.h b/chromium/ui/gfx/display.h index c6df7e703ad..33db48b77a3 100644 --- a/chromium/ui/gfx/display.h +++ b/chromium/ui/gfx/display.h @@ -21,7 +21,7 @@ namespace gfx { // system. For platforms that support DIP (density independent pixel), // |bounds()| and |work_area| will return values in DIP coordinate // system, not in backing pixels. -class GFX_EXPORT Display { +class GFX_EXPORT Display final { public: // Screen Rotation in clock-wise degrees. // This enum corresponds to DisplayRotationDefaultProto::Rotation in @@ -38,11 +38,13 @@ class GFX_EXPORT Display { // RotationSource allows for the tracking of a Rotation per source of the // change. ROTATION_SOURCE_ACTIVE is the current rotation of the display. // Rotation changes not due to an accelerometer, nor the user, are to use this - // source directly. + // source directly. ROTATION_SOURCE_UNKNOWN is when no rotation source has + // been provided. enum RotationSource { ROTATION_SOURCE_ACCELEROMETER = 0, ROTATION_SOURCE_ACTIVE, ROTATION_SOURCE_USER, + ROTATION_SOURCE_UNKNOWN, }; // Touch support for the display. @@ -58,6 +60,7 @@ class GFX_EXPORT Display { Display(); explicit Display(int64_t id); Display(int64_t id, const Rect& bounds); + Display(const Display& other); ~Display(); // Returns the forced device scale factor, which is given by @@ -154,6 +157,11 @@ class GFX_EXPORT Display { TouchSupport touch_support_; }; +// This is declared here for use in gtest-based unit tests but is defined in +// the 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. +void PrintTo(const Display& display, ::std::ostream* os); + } // namespace gfx #endif // UI_GFX_DISPLAY_H_ diff --git a/chromium/ui/gfx/display_finder.cc b/chromium/ui/gfx/display_finder.cc new file mode 100644 index 00000000000..af27f1b3247 --- /dev/null +++ b/chromium/ui/gfx/display_finder.cc @@ -0,0 +1,50 @@ +// 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/display_finder.h" + +#include <limits> + +#include "base/logging.h" +#include "ui/gfx/display.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" + +namespace gfx { + +const Display* FindDisplayNearestPoint(const std::vector<Display>& displays, + const Point& point) { + DCHECK(!displays.empty()); + int min_distance = std::numeric_limits<int>::max(); + const Display* nearest_display = nullptr; + for (const auto& display : displays) { + const int distance = display.bounds().ManhattanDistanceToPoint(point); + if (distance < min_distance) { + min_distance = distance; + nearest_display = &display; + } + } + // There should always be at least one display that is less than INT_MAX away. + DCHECK(nearest_display); + return nearest_display; +} + +const Display* FindDisplayWithBiggestIntersection( + const std::vector<Display>& displays, + const Rect& rect) { + DCHECK(!displays.empty()); + int max_area = 0; + const Display* matching = nullptr; + for (const auto& display : displays) { + const Rect intersect = IntersectRects(display.bounds(), rect); + const int area = intersect.width() * intersect.height(); + if (area > max_area) { + max_area = area; + matching = &display; + } + } + return matching; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/display_finder.h b/chromium/ui/gfx/display_finder.h new file mode 100644 index 00000000000..2fb81f6eb44 --- /dev/null +++ b/chromium/ui/gfx/display_finder.h @@ -0,0 +1,31 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_DISPLAY_FINDER_H_ +#define UI_GFX_DISPLAY_FINDER_H_ + +#include <vector> + +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +class Display; +class Point; +class Rect; + +// Returns the display in |displays| closest to |point|. +GFX_EXPORT const Display* FindDisplayNearestPoint( + const std::vector<Display>& displays, + const Point& point); + +// Returns the display in |displays| with the biggest intersection of |rect|. +// If none of the displays intersect |rect| null is returned. +GFX_EXPORT const Display* FindDisplayWithBiggestIntersection( + const std::vector<Display>& displays, + const Rect& rect); + +} // namespace gfx + +#endif // UI_GFX_DISPLAY_FINDER_H_ diff --git a/chromium/ui/gfx/font_fallback_linux.cc b/chromium/ui/gfx/font_fallback_linux.cc index 919ac649af6..ad690c594f5 100644 --- a/chromium/ui/gfx/font_fallback_linux.cc +++ b/chromium/ui/gfx/font_fallback_linux.cc @@ -2,15 +2,17 @@ // 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 "ui/gfx/font_fallback_linux.h" #include <fontconfig/fontconfig.h> #include <map> +#include <memory> #include <string> #include <vector> #include "base/lazy_instance.h" +#include "base/memory/ptr_util.h" #include "ui/gfx/font.h" namespace gfx { @@ -62,4 +64,184 @@ std::vector<Font> GetFallbackFonts(const Font& font) { return *fallback_fonts; } +namespace { + +class CachedFont { + public: + // Note: We pass the charset explicitly as callers + // should not create CachedFont entries without knowing + // that the FcPattern contains a valid charset. + CachedFont(FcPattern* pattern, FcCharSet* char_set) + : supported_characters_(char_set) { + DCHECK(pattern); + DCHECK(char_set); + fallback_font_.name = GetFontName(pattern); + fallback_font_.filename = GetFontFilename(pattern); + fallback_font_.ttc_index = GetFontTtcIndex(pattern); + fallback_font_.is_bold = IsFontBold(pattern); + fallback_font_.is_italic = IsFontItalic(pattern); + } + + const FallbackFontData& fallback_font() const { return fallback_font_; } + + bool HasGlyphForCharacter(UChar32 c) const { + return supported_characters_ && FcCharSetHasChar(supported_characters_, c); + } + + private: + static std::string GetFontName(FcPattern* pattern) { + FcChar8* familyName = nullptr; + if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch) + return std::string(); + return std::string(reinterpret_cast<const char*>(familyName)); + } + + static std::string GetFontFilename(FcPattern* pattern) { + FcChar8* c_filename = nullptr; + if (FcPatternGetString(pattern, FC_FILE, 0, &c_filename) != FcResultMatch) + return std::string(); + return std::string(reinterpret_cast<const char*>(c_filename)); + } + + static int GetFontTtcIndex(FcPattern* pattern) { + int ttcIndex = -1; + if (FcPatternGetInteger(pattern, FC_INDEX, 0, &ttcIndex) != FcResultMatch || + ttcIndex < 0) + return 0; + return ttcIndex; + } + + static bool IsFontBold(FcPattern* pattern) { + int weight = 0; + if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch) + return false; + return weight >= FC_WEIGHT_BOLD; + } + + static bool IsFontItalic(FcPattern* pattern) { + int slant = 0; + if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch) + return false; + return slant != FC_SLANT_ROMAN; + } + + FallbackFontData fallback_font_; + // supported_characters_ is owned by the parent + // FcFontSet and should never be freed. + FcCharSet* supported_characters_; +}; + +class CachedFontSet { + public: + // CachedFontSet takes ownership of the passed FcFontSet. + static std::unique_ptr<CachedFontSet> CreateForLocale( + const std::string& locale) { + FcFontSet* font_set = CreateFcFontSetForLocale(locale); + return base::WrapUnique(new CachedFontSet(font_set)); + } + + ~CachedFontSet() { + fallback_list_.clear(); + FcFontSetDestroy(font_set_); + } + + FallbackFontData GetFallbackFontForChar(UChar32 c) { + for (const auto& cached_font : fallback_list_) { + if (cached_font.HasGlyphForCharacter(c)) + return cached_font.fallback_font(); + } + // The previous code just returned garbage if the user didn't + // have the necessary fonts, this seems better than garbage. + // Current callers happen to ignore any values with an empty family string. + return FallbackFontData(); + } + + private: + static FcFontSet* CreateFcFontSetForLocale(const std::string& locale) { + FcPattern* pattern = FcPatternCreate(); + + if (!locale.empty()) { + // FcChar* is unsigned char* so we have to cast. + FcPatternAddString(pattern, FC_LANG, + reinterpret_cast<const FcChar8*>(locale.c_str())); + } + + FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + if (locale.empty()) + FcPatternDel(pattern, FC_LANG); + + // The result parameter returns if any fonts were found. + // We already handle 0 fonts correctly, so we ignore the param. + FcResult result; + FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); + FcPatternDestroy(pattern); + + // The caller will take ownership of this FcFontSet. + return font_set; + } + + CachedFontSet(FcFontSet* font_set) : font_set_(font_set) { + FillFallbackList(); + } + + void FillFallbackList() { + DCHECK(fallback_list_.empty()); + if (!font_set_) + return; + + for (int i = 0; i < font_set_->nfont; ++i) { + FcPattern* pattern = font_set_->fonts[i]; + + // Ignore any bitmap fonts users may still have installed from last + // century. + FcBool is_scalable; + if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != + FcResultMatch || + !is_scalable) + continue; + + // Ignore any fonts FontConfig knows about, but that we don't have + // permission to read. + FcChar8* c_filename; + if (FcPatternGetString(pattern, FC_FILE, 0, &c_filename) != FcResultMatch) + continue; + if (access(reinterpret_cast<char*>(c_filename), R_OK)) + continue; + + // Make sure this font can tell us what characters it has glyphs for. + FcCharSet* char_set; + if (FcPatternGetCharSet(pattern, FC_CHARSET, 0, &char_set) != + FcResultMatch) + continue; + + fallback_list_.emplace_back(pattern, char_set); + } + } + + FcFontSet* font_set_; // Owned by this object. + // CachedFont has a FcCharset* which points into the FcFontSet. + // If the FcFontSet is ever destroyed, the fallback list + // must be cleared first. + std::vector<CachedFont> fallback_list_; + + DISALLOW_COPY_AND_ASSIGN(CachedFontSet); +}; + +typedef std::map<std::string, std::unique_ptr<CachedFontSet>> FontSetCache; +base::LazyInstance<FontSetCache>::Leaky g_font_sets_by_locale = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +FallbackFontData GetFallbackFontForChar(UChar32 c, const std::string& locale) { + auto& cached_font_set = g_font_sets_by_locale.Get()[locale]; + if (!cached_font_set) + cached_font_set = CachedFontSet::CreateForLocale(locale); + return cached_font_set->GetFallbackFontForChar(c); +} + } // namespace gfx diff --git a/chromium/ui/gfx/font_fallback_linux.h b/chromium/ui/gfx/font_fallback_linux.h new file mode 100644 index 00000000000..ac9cd572419 --- /dev/null +++ b/chromium/ui/gfx/font_fallback_linux.h @@ -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. + +#ifndef UI_GFX_FONT_FALLBACK_LINUX_H_ +#define UI_GFX_FONT_FALLBACK_LINUX_H_ + +#include <string> + +#include "third_party/icu/source/common/unicode/uchar.h" +#include "ui/gfx/font_fallback.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// Return a font family which provides a glyph for the Unicode code point +// specified by character. +// c: a UTF-32 code point +// preferred_locale: preferred locale identifier for the |characters| +// (e.g. "en", "ja", "zh-CN") +// +// Returns: the font family instance. The instance has an empty font name if the +// request could not be satisfied. +// +// Previously blink::WebFontInfo::fallbackFontForChar. +struct FallbackFontData { + std::string name; + std::string filename; + int ttc_index = 0; + bool is_bold = false; + bool is_italic = false; +}; +GFX_EXPORT FallbackFontData +GetFallbackFontForChar(UChar32 c, const std::string& preferred_locale); + +} // namespace gfx + +#endif // UI_GFX_FONT_FALLBACK_LINUX_H_ diff --git a/chromium/ui/gfx/font_render_params.cc b/chromium/ui/gfx/font_render_params.cc index a434379ee10..7b8543560c8 100644 --- a/chromium/ui/gfx/font_render_params.cc +++ b/chromium/ui/gfx/font_render_params.cc @@ -17,6 +17,8 @@ FontRenderParams::FontRenderParams() subpixel_rendering(SUBPIXEL_RENDERING_NONE) { } +FontRenderParams::FontRenderParams(const FontRenderParams& other) = default; + FontRenderParams::~FontRenderParams() {} // static @@ -61,6 +63,9 @@ FontRenderParamsQuery::FontRenderParamsQuery() device_scale_factor(0) { } +FontRenderParamsQuery::FontRenderParamsQuery( + const FontRenderParamsQuery& other) = default; + FontRenderParamsQuery::~FontRenderParamsQuery() {} } // namespace gfx diff --git a/chromium/ui/gfx/font_render_params.h b/chromium/ui/gfx/font_render_params.h index 989f9aa129a..7a294bdc3cd 100644 --- a/chromium/ui/gfx/font_render_params.h +++ b/chromium/ui/gfx/font_render_params.h @@ -17,6 +17,7 @@ namespace gfx { // A collection of parameters describing how text should be rendered on Linux. struct GFX_EXPORT FontRenderParams { FontRenderParams(); + FontRenderParams(const FontRenderParams& other); ~FontRenderParams(); // Level of hinting to be applied. @@ -73,6 +74,7 @@ struct GFX_EXPORT FontRenderParams { // A query used to determine the appropriate FontRenderParams. struct GFX_EXPORT FontRenderParamsQuery { FontRenderParamsQuery(); + FontRenderParamsQuery(const FontRenderParamsQuery& other); ~FontRenderParamsQuery(); bool is_empty() const { @@ -106,9 +108,9 @@ GFX_EXPORT FontRenderParams GetFontRenderParams( GFX_EXPORT void ClearFontRenderParamsCacheForTest(); #endif -#if defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) || defined(OS_LINUX) // Gets the device scale factor to query the FontRenderParams. -float GetFontRenderParamsDeviceScaleFactor(); +GFX_EXPORT float GetFontRenderParamsDeviceScaleFactor(); // Sets the device scale factor for FontRenderParams to decide // if it should enable subpixel positioning. diff --git a/chromium/ui/gfx/font_render_params_linux.cc b/chromium/ui/gfx/font_render_params_linux.cc index a9afcf344db..b2e8d5eb774 100644 --- a/chromium/ui/gfx/font_render_params_linux.cc +++ b/chromium/ui/gfx/font_render_params_linux.cc @@ -19,21 +19,17 @@ #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" #include "build/build_config.h" -#include "ui/gfx/display.h" #include "ui/gfx/font.h" #include "ui/gfx/linux_font_delegate.h" -#include "ui/gfx/screen.h" #include "ui/gfx/switches.h" namespace gfx { namespace { -#if defined(OS_CHROMEOS) -// A device scale factor for an internal display (if any) -// that is used to determine if subpixel positioning should be used. -float device_scale_factor_for_internal_display = 1.0f; -#endif +// A device scale factor used to determine if subpixel positioning +// should be used. +float device_scale_factor_ = 1.0f; // Number of recent GetFontRenderParams() results to cache. const size_t kCacheSize = 256; @@ -204,19 +200,9 @@ uint32_t HashFontRenderParamsQuery(const FontRenderParamsQuery& query) { FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query, std::string* family_out) { FontRenderParamsQuery actual_query(query); - if (actual_query.device_scale_factor == 0) { -#if defined(OS_CHROMEOS) - actual_query.device_scale_factor = device_scale_factor_for_internal_display; -#else - // Linux does not support per-display DPI, so we use a slightly simpler - // code path than on Chrome OS to figure out the device scale factor. - gfx::Screen* screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE); - if (screen) { - gfx::Display display = screen->GetPrimaryDisplay(); - actual_query.device_scale_factor = display.device_scale_factor(); - } -#endif - } + if (actual_query.device_scale_factor == 0) + actual_query.device_scale_factor = device_scale_factor_; + const uint32_t hash = HashFontRenderParamsQuery(actual_query); SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer(); @@ -279,14 +265,12 @@ void ClearFontRenderParamsCacheForTest() { synchronized_cache->cache.Clear(); } -#if defined(OS_CHROMEOS) float GetFontRenderParamsDeviceScaleFactor() { - return device_scale_factor_for_internal_display; + return device_scale_factor_; } void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor) { - device_scale_factor_for_internal_display = device_scale_factor; + device_scale_factor_ = device_scale_factor; } -#endif } // namespace gfx diff --git a/chromium/ui/gfx/font_render_params_linux_unittest.cc b/chromium/ui/gfx/font_render_params_linux_unittest.cc index 27d1ff3b206..63601d1b3b5 100644 --- a/chromium/ui/gfx/font_render_params_linux_unittest.cc +++ b/chromium/ui/gfx/font_render_params_linux_unittest.cc @@ -279,7 +279,6 @@ TEST_F(FontRenderParamsTest, ForceFullHintingWhenAntialiasingIsDisabled) { EXPECT_FALSE(params.subpixel_positioning); } -#if defined(OS_CHROMEOS) TEST_F(FontRenderParamsTest, ForceSubpixelPositioning) { { FontRenderParams params = @@ -299,7 +298,6 @@ TEST_F(FontRenderParamsTest, ForceSubpixelPositioning) { SetFontRenderParamsDeviceScaleFactor(1.0f); } } -#endif TEST_F(FontRenderParamsTest, OnlySetConfiguredValues) { // Configure the LinuxFontDelegate (which queries GtkSettings on desktop diff --git a/chromium/ui/gfx/generic_shared_memory_id.h b/chromium/ui/gfx/generic_shared_memory_id.h index 51f553b2dda..b18c869e2cc 100644 --- a/chromium/ui/gfx/generic_shared_memory_id.h +++ b/chromium/ui/gfx/generic_shared_memory_id.h @@ -8,6 +8,8 @@ #include <stddef.h> #include <stdint.h> +#include "base/containers/hash_tables.h" +#include "base/hash.h" #include "base/trace_event/memory_allocator_dump.h" #include "ui/gfx/gfx_export.h" @@ -58,7 +60,7 @@ template <typename Second> struct hash<std::pair<gfx::GenericSharedMemoryId, Second>> { size_t operator()( const std::pair<gfx::GenericSharedMemoryId, Second>& pair) const { - return base::HashPair(pair.first.id, pair.second); + return base::HashInts(pair.first.id, pair.second); } }; diff --git a/chromium/ui/gfx/geometry/cubic_bezier.cc b/chromium/ui/gfx/geometry/cubic_bezier.cc index fe6dfcc7ff1..16acfc7c942 100644 --- a/chromium/ui/gfx/geometry/cubic_bezier.cc +++ b/chromium/ui/gfx/geometry/cubic_bezier.cc @@ -11,129 +11,93 @@ namespace gfx { -namespace { - static const double kBezierEpsilon = 1e-7; -static const int MAX_STEPS = 30; - -static double eval_bezier(double p1, double p2, double t) { - const double p1_times_3 = 3.0 * p1; - const double p2_times_3 = 3.0 * p2; - const double h3 = p1_times_3; - const double h1 = p1_times_3 - p2_times_3 + 1.0; - const double h2 = p2_times_3 - 6.0 * p1; - return t * (t * (t * h1 + h2) + h3); -} -static double eval_bezier_derivative(double p1, double p2, double t) { - const double h1 = 9.0 * p1 - 9.0 * p2 + 3.0; - const double h2 = 6.0 * p2 - 12.0 * p1; - const double h3 = 3.0 * p1; - return t * (t * h1 + h2) + h3; +CubicBezier::CubicBezier(double p1x, double p1y, double p2x, double p2y) { + InitCoefficients(p1x, p1y, p2x, p2y); + InitGradients(p1x, p1y, p2x, p2y); + InitRange(p1y, p2y); } -// Finds t such that eval_bezier(x1, x2, t) = x. -// There is a unique solution if x1 and x2 lie within (0, 1). -static double bezier_interp(double x1, - double x2, - double x) { - DCHECK_GE(1.0, x1); - DCHECK_LE(0.0, x1); - DCHECK_GE(1.0, x2); - DCHECK_LE(0.0, x2); - - x1 = std::min(std::max(x1, 0.0), 1.0); - x2 = std::min(std::max(x2, 0.0), 1.0); - x = std::min(std::max(x, 0.0), 1.0); - - // We're just going to do bisection for now (for simplicity), but we could - // easily do some newton steps if this turns out to be a bottleneck. - double t = 0.0; - double step = 1.0; - for (int i = 0; i < MAX_STEPS; ++i, step *= 0.5) { - const double error = eval_bezier(x1, x2, t) - x; - if (std::abs(error) < kBezierEpsilon) - break; - t += error > 0.0 ? -step : step; - } - - // We should have terminated the above loop because we got close to x, not - // because we exceeded MAX_STEPS. Do a DCHECK here to confirm. - DCHECK_GT(kBezierEpsilon, std::abs(eval_bezier(x1, x2, t) - x)); - - return t; +CubicBezier::CubicBezier(const CubicBezier& other) = default; + +void CubicBezier::InitCoefficients(double p1x, + double p1y, + double p2x, + double p2y) { + // Calculate the polynomial coefficients, implicit first and last control + // points are (0,0) and (1,1). + cx_ = 3.0 * p1x; + bx_ = 3.0 * (p2x - p1x) - cx_; + ax_ = 1.0 - cx_ - bx_; + + cy_ = 3.0 * p1y; + by_ = 3.0 * (p2y - p1y) - cy_; + ay_ = 1.0 - cy_ - by_; } -} // namespace - -CubicBezier::CubicBezier(double x1, double y1, double x2, double y2) - : x1_(x1), - y1_(y1), - x2_(x2), - y2_(y2) { - InitGradients(); -} - -CubicBezier::~CubicBezier() { -} - -void CubicBezier::InitGradients() { - if (x1_ > 0) - start_gradient_ = y1_ / x1_; - else if (!y1_ && x2_ > 0) - start_gradient_ = y2_ / x2_; +void CubicBezier::InitGradients(double p1x, + double p1y, + double p2x, + double p2y) { + // End-point gradients are used to calculate timing function results + // outside the range [0, 1]. + // + // There are three possibilities for the gradient at each end: + // (1) the closest control point is not horizontally coincident with regard to + // (0, 0) or (1, 1). In this case the line between the end point and + // the control point is tangent to the bezier at the end point. + // (2) the closest control point is coincident with the end point. In + // this case the line between the end point and the far control + // point is tangent to the bezier at the end point. + // (3) the closest control point is horizontally coincident with the end + // point, but vertically distinct. In this case the gradient at the + // end point is Infinite. However, this causes issues when + // interpolating. As a result, we break down to a simple case of + // 0 gradient under these conditions. + + if (p1x > 0) + start_gradient_ = p1y / p1x; + else if (!p1y && p2x > 0) + start_gradient_ = p2y / p2x; else start_gradient_ = 0; - if (x2_ < 1) - end_gradient_ = (y2_ - 1) / (x2_ - 1); - else if (x2_ == 1 && x1_ < 1) - end_gradient_ = (y1_ - 1) / (x1_ - 1); + if (p2x < 1) + end_gradient_ = (p2y - 1) / (p2x - 1); + else if (p2x == 1 && p1x < 1) + end_gradient_ = (p1y - 1) / (p1x - 1); else end_gradient_ = 0; } -double CubicBezier::Solve(double x) const { - if (x < 0) - return start_gradient_ * x; - if (x > 1) - return 1.0 + end_gradient_ * (x - 1.0); - - return eval_bezier(y1_, y2_, bezier_interp(x1_, x2_, x)); -} - -double CubicBezier::Slope(double x) const { - double t = bezier_interp(x1_, x2_, x); - double dx_dt = eval_bezier_derivative(x1_, x2_, t); - double dy_dt = eval_bezier_derivative(y1_, y2_, t); - return dy_dt / dx_dt; -} - -void CubicBezier::Range(double* min, double* max) const { - *min = 0; - *max = 1; - if (0 <= y1_ && y1_ < 1 && 0 <= y2_ && y2_ <= 1) +void CubicBezier::InitRange(double p1y, double p2y) { + range_min_ = 0; + range_max_ = 1; + if (0 <= p1y && p1y < 1 && 0 <= p2y && p2y <= 1) return; - // Represent the function's derivative in the form at^2 + bt + c. + const double epsilon = kBezierEpsilon; + + // Represent the function's derivative in the form at^2 + bt + c + // as in sampleCurveDerivativeY. // (Technically this is (dy/dt)*(1/3), which is suitable for finding zeros // but does not actually give the slope of the curve.) - double a = 3 * (y1_ - y2_) + 1; - double b = 2 * (y2_ - 2 * y1_); - double c = y1_; + const double a = 3.0 * ay_; + const double b = 2.0 * by_; + const double c = cy_; // Check if the derivative is constant. - if (std::abs(a) < kBezierEpsilon && - std::abs(b) < kBezierEpsilon) + if (std::abs(a) < epsilon && std::abs(b) < epsilon) return; // Zeros of the function's derivative. - double t_1 = 0; - double t_2 = 0; + double t1 = 0; + double t2 = 0; - if (std::abs(a) < kBezierEpsilon) { + if (std::abs(a) < epsilon) { // The function's derivative is linear. - t_1 = -c / b; + t1 = -c / b; } else { // The function's derivative is a quadratic. We find the zeros of this // quadratic using the quadratic formula. @@ -141,21 +105,79 @@ void CubicBezier::Range(double* min, double* max) const { if (discriminant < 0) return; double discriminant_sqrt = sqrt(discriminant); - t_1 = (-b + discriminant_sqrt) / (2 * a); - t_2 = (-b - discriminant_sqrt) / (2 * a); + t1 = (-b + discriminant_sqrt) / (2 * a); + t2 = (-b - discriminant_sqrt) / (2 * a); + } + + double sol1 = 0; + double sol2 = 0; + + if (0 < t1 && t1 < 1) + sol1 = SampleCurveY(t1); + + if (0 < t2 && t2 < 1) + sol2 = SampleCurveY(t2); + + range_min_ = std::min(std::min(range_min_, sol1), sol2); + range_max_ = std::max(std::max(range_max_, sol1), sol2); +} + +double CubicBezier::SolveCurveX(double x, double epsilon) const { + DCHECK_GE(x, 0.0); + DCHECK_LE(x, 1.0); + + double t0; + double t1; + double t2; + double x2; + double d2; + int i; + + // First try a few iterations of Newton's method -- normally very fast. + for (t2 = x, i = 0; i < 8; i++) { + x2 = SampleCurveX(t2) - x; + if (fabs(x2) < epsilon) + return t2; + d2 = SampleCurveDerivativeX(t2); + if (fabs(d2) < 1e-6) + break; + t2 = t2 - x2 / d2; } - double sol_1 = 0; - double sol_2 = 0; + // Fall back to the bisection method for reliability. + t0 = 0.0; + t1 = 1.0; + t2 = x; + + while (t0 < t1) { + x2 = SampleCurveX(t2); + if (fabs(x2 - x) < epsilon) + return t2; + if (x > x2) + t0 = t2; + else + t1 = t2; + t2 = (t1 - t0) * .5 + t0; + } - if (0 < t_1 && t_1 < 1) - sol_1 = eval_bezier(y1_, y2_, t_1); + // Failure. + return t2; +} - if (0 < t_2 && t_2 < 1) - sol_2 = eval_bezier(y1_, y2_, t_2); +double CubicBezier::Solve(double x) const { + return SolveWithEpsilon(x, kBezierEpsilon); +} - *min = std::min(std::min(*min, sol_1), sol_2); - *max = std::max(std::max(*max, sol_1), sol_2); +double CubicBezier::SlopeWithEpsilon(double x, double epsilon) const { + x = std::min(std::max(x, 0.0), 1.0); + double t = SolveCurveX(x, epsilon); + double dx = SampleCurveDerivativeX(t); + double dy = SampleCurveDerivativeY(t); + return dy / dx; +} + +double CubicBezier::Slope(double x) const { + return SlopeWithEpsilon(x, kBezierEpsilon); } } // namespace gfx diff --git a/chromium/ui/gfx/geometry/cubic_bezier.h b/chromium/ui/gfx/geometry/cubic_bezier.h index 5a885f35db2..f32776b82df 100644 --- a/chromium/ui/gfx/geometry/cubic_bezier.h +++ b/chromium/ui/gfx/geometry/cubic_bezier.h @@ -12,30 +12,76 @@ namespace gfx { class GFX_EXPORT CubicBezier { public: - CubicBezier(double x1, double y1, double x2, double y2); - ~CubicBezier(); + CubicBezier(double p1x, double p1y, double p2x, double p2y); + CubicBezier(const CubicBezier& other); - // Returns an approximation of y at the given x. + double SampleCurveX(double t) const { + // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. + return ((ax_ * t + bx_) * t + cx_) * t; + } + + double SampleCurveY(double t) const { + return ((ay_ * t + by_) * t + cy_) * t; + } + + double SampleCurveDerivativeX(double t) const { + return (3.0 * ax_ * t + 2.0 * bx_) * t + cx_; + } + + double SampleCurveDerivativeY(double t) const { + return (3.0 * ay_ * t + 2.0 * by_) * t + cy_; + } + + // Given an x value, find a parametric value it came from. + // x must be in [0, 1] range. Doesn't use gradients. + double SolveCurveX(double x, double epsilon) const; + + // Evaluates y at the given x with default epsilon. double Solve(double x) const; + // Evaluates y at the given x. The epsilon parameter provides a hint as to the + // required accuracy and is not guaranteed. Uses gradients if x is + // out of [0, 1] range. + double SolveWithEpsilon(double x, double epsilon) const { + if (x < 0.0) + return 0.0 + start_gradient_ * x; + if (x > 1.0) + return 1.0 + end_gradient_ * (x - 1.0); + return SampleCurveY(SolveCurveX(x, epsilon)); + } - // Returns an approximation of dy/dx at the given x. + // Returns an approximation of dy/dx at the given x with default epsilon. double Slope(double x) const; + // Returns an approximation of dy/dx at the given x. + // Clamps x to range [0, 1]. + double SlopeWithEpsilon(double x, double epsilon) const; // Sets |min| and |max| to the bezier's minimum and maximium y values in the // interval [0, 1]. - void Range(double* min, double* max) const; + // TODO(loyso): Implement this as two independent getters. + void Range(double* min, double* max) const { + *min = range_min_; + *max = range_max_; + } private: - void InitGradients(); + void InitCoefficients(double p1x, double p1y, double p2x, double p2y); + void InitGradients(double p1x, double p1y, double p2x, double p2y); + void InitRange(double p1y, double p2y); - double x1_; - double y1_; - double x2_; - double y2_; + double ax_; + double bx_; + double cx_; + + double ay_; + double by_; + double cy_; double start_gradient_; double end_gradient_; + double range_min_; + double range_max_; + DISALLOW_ASSIGN(CubicBezier); }; diff --git a/chromium/ui/gfx/geometry/cubic_bezier_unittest.cc b/chromium/ui/gfx/geometry/cubic_bezier_unittest.cc index 9ade0daf42f..a69836185c9 100644 --- a/chromium/ui/gfx/geometry/cubic_bezier_unittest.cc +++ b/chromium/ui/gfx/geometry/cubic_bezier_unittest.cc @@ -35,6 +35,15 @@ TEST(CubicBezierTest, Basic) { EXPECT_NEAR(function.Solve(0.9), 0.96021, epsilon); EXPECT_NEAR(function.Solve(0.95), 0.98863, epsilon); EXPECT_NEAR(function.Solve(1), 1, epsilon); + + CubicBezier basic_use(0.5, 1.0, 0.5, 1.0); + EXPECT_EQ(0.875, basic_use.Solve(0.5)); + + CubicBezier overshoot(0.5, 2.0, 0.5, 2.0); + EXPECT_EQ(1.625, overshoot.Solve(0.5)); + + CubicBezier undershoot(0.5, -1.0, 0.5, -1.0); + EXPECT_EQ(-0.625, undershoot.Solve(0.5)); } // Tests that solving the bezier works with knots with y not in (0, 1). @@ -141,6 +150,7 @@ TEST(CubicBezierTest, Slope) { double epsilon = 0.00015; + EXPECT_NEAR(function.Slope(-0.1), 0, epsilon); EXPECT_NEAR(function.Slope(0), 0, epsilon); EXPECT_NEAR(function.Slope(0.05), 0.42170, epsilon); EXPECT_NEAR(function.Slope(0.1), 0.69778, epsilon); @@ -162,6 +172,7 @@ TEST(CubicBezierTest, Slope) { EXPECT_NEAR(function.Slope(0.9), 0.69778, epsilon); EXPECT_NEAR(function.Slope(0.95), 0.42170, epsilon); EXPECT_NEAR(function.Slope(1), 0, epsilon); + EXPECT_NEAR(function.Slope(1.1), 0, epsilon); } TEST(CubicBezierTest, InputOutOfRange) { @@ -169,25 +180,33 @@ TEST(CubicBezierTest, InputOutOfRange) { EXPECT_EQ(-2.0, simple.Solve(-1.0)); EXPECT_EQ(1.0, simple.Solve(2.0)); - CubicBezier coincidentEndpoints(0.0, 0.0, 1.0, 1.0); - EXPECT_EQ(-1.0, coincidentEndpoints.Solve(-1.0)); - EXPECT_EQ(2.0, coincidentEndpoints.Solve(2.0)); + CubicBezier at_edge_of_range(0.5, 1.0, 0.5, 1.0); + EXPECT_EQ(0.0, at_edge_of_range.Solve(0.0)); + EXPECT_EQ(1.0, at_edge_of_range.Solve(1.0)); + + CubicBezier large_epsilon(0.5, 1.0, 0.5, 1.0); + EXPECT_EQ(-2.0, large_epsilon.SolveWithEpsilon(-1.0, 1.0)); + EXPECT_EQ(1.0, large_epsilon.SolveWithEpsilon(2.0, 1.0)); + + CubicBezier coincident_endpoints(0.0, 0.0, 1.0, 1.0); + EXPECT_EQ(-1.0, coincident_endpoints.Solve(-1.0)); + EXPECT_EQ(2.0, coincident_endpoints.Solve(2.0)); - CubicBezier verticalGradient(0.0, 1.0, 1.0, 0.0); - EXPECT_EQ(0.0, verticalGradient.Solve(-1.0)); - EXPECT_EQ(1.0, verticalGradient.Solve(2.0)); + CubicBezier vertical_gradient(0.0, 1.0, 1.0, 0.0); + EXPECT_EQ(0.0, vertical_gradient.Solve(-1.0)); + EXPECT_EQ(1.0, vertical_gradient.Solve(2.0)); - CubicBezier distinctEndpoints(0.1, 0.2, 0.8, 0.8); - EXPECT_EQ(-2.0, distinctEndpoints.Solve(-1.0)); - EXPECT_EQ(2.0, distinctEndpoints.Solve(2.0)); + CubicBezier distinct_endpoints(0.1, 0.2, 0.8, 0.8); + EXPECT_EQ(-2.0, distinct_endpoints.Solve(-1.0)); + EXPECT_EQ(2.0, distinct_endpoints.Solve(2.0)); - CubicBezier coincidentEndpoint(0.0, 0.0, 0.8, 0.8); - EXPECT_EQ(-1.0, coincidentEndpoint.Solve(-1.0)); - EXPECT_EQ(2.0, coincidentEndpoint.Solve(2.0)); + CubicBezier coincident_endpoint(0.0, 0.0, 0.8, 0.8); + EXPECT_EQ(-1.0, coincident_endpoint.Solve(-1.0)); + EXPECT_EQ(2.0, coincident_endpoint.Solve(2.0)); - CubicBezier threeCoincidentPoints(0.0, 0.0, 0.0, 0.0); - EXPECT_EQ(0, threeCoincidentPoints.Solve(-1.0)); - EXPECT_EQ(2.0, threeCoincidentPoints.Solve(2.0)); + CubicBezier three_coincident_points(0.0, 0.0, 0.0, 0.0); + EXPECT_EQ(0, three_coincident_points.Solve(-1.0)); + EXPECT_EQ(2.0, three_coincident_points.Solve(2.0)); } diff --git a/chromium/ui/gfx/geometry/insets.cc b/chromium/ui/gfx/geometry/insets.cc index 46fb84ce233..13a05b31dd0 100644 --- a/chromium/ui/gfx/geometry/insets.cc +++ b/chromium/ui/gfx/geometry/insets.cc @@ -8,7 +8,12 @@ namespace gfx { -Insets::Insets() : Insets(0, 0, 0, 0) {} +Insets::Insets() : Insets(0) {} + +Insets::Insets(int all) : Insets(all, all, all, all) {} + +Insets::Insets(int vertical, int horizontal) + : Insets(vertical, horizontal, vertical, horizontal) {} Insets::Insets(int top, int left, int bottom, int right) : top_(top), left_(left), bottom_(bottom), right_(right) {} diff --git a/chromium/ui/gfx/geometry/insets.h b/chromium/ui/gfx/geometry/insets.h index 0466d947704..2fc0d62412b 100644 --- a/chromium/ui/gfx/geometry/insets.h +++ b/chromium/ui/gfx/geometry/insets.h @@ -12,10 +12,11 @@ namespace gfx { -// An integer version of gfx::Insets. class GFX_EXPORT Insets { public: Insets(); + explicit Insets(int all); + Insets(int vertical, int horizontal); Insets(int top, int left, int bottom, int right); ~Insets(); diff --git a/chromium/ui/gfx/geometry/insets_f.cc b/chromium/ui/gfx/geometry/insets_f.cc index 24ccd627bdb..e4368913ccd 100644 --- a/chromium/ui/gfx/geometry/insets_f.cc +++ b/chromium/ui/gfx/geometry/insets_f.cc @@ -8,7 +8,12 @@ namespace gfx { -InsetsF::InsetsF() : InsetsF(0.f, 0.f, 0.f, 0.f) {} +InsetsF::InsetsF() : InsetsF(0.f) {} + +InsetsF::InsetsF(float all) : InsetsF(all, all, all, all) {} + +InsetsF::InsetsF(float vertical, float horizontal) + : InsetsF(vertical, horizontal, vertical, horizontal) {} InsetsF::InsetsF(float top, float left, float bottom, float right) : top_(top), left_(left), bottom_(bottom), right_(right) {} diff --git a/chromium/ui/gfx/geometry/insets_f.h b/chromium/ui/gfx/geometry/insets_f.h index 90592d774a0..7ff2cec2e28 100644 --- a/chromium/ui/gfx/geometry/insets_f.h +++ b/chromium/ui/gfx/geometry/insets_f.h @@ -11,11 +11,14 @@ namespace gfx { -// A floating versin of gfx::Insets. +// A floating point version of gfx::Insets. class GFX_EXPORT InsetsF { public: InsetsF(); + explicit InsetsF(float all); + InsetsF(float vertical, float horizontal); InsetsF(float top, float left, float bottom, float right); + ~InsetsF(); float top() const { return top_; } diff --git a/chromium/ui/gfx/gfx.gyp b/chromium/ui/gfx/gfx.gyp index 673d1cfb961..e7936ce5bc5 100644 --- a/chromium/ui/gfx/gfx.gyp +++ b/chromium/ui/gfx/gfx.gyp @@ -113,6 +113,8 @@ 'animation/animation_container_element.h', 'animation/animation_container_observer.h', 'animation/animation_delegate.h', + 'animation/animation_mac.mm', + 'animation/animation_win.cc', 'animation/linear_animation.cc', 'animation/linear_animation.h', 'animation/multi_animation.cc', @@ -153,6 +155,8 @@ 'display.h', 'display_change_notifier.cc', 'display_change_notifier.h', + 'display_finder.cc', + 'display_finder.h', 'display_observer.cc', 'display_observer.h', 'favicon_size.cc', @@ -161,6 +165,7 @@ 'font.h', 'font_fallback.h', 'font_fallback_linux.cc', + 'font_fallback_linux.h', 'font_fallback_mac.mm', 'font_fallback_win.cc', 'font_fallback_win.h', @@ -197,6 +202,7 @@ 'image/image_skia_operations.h', 'image/image_skia_rep.cc', 'image/image_skia_rep.h', + 'image/image_skia_source.cc', 'image/image_skia_source.h', 'image/image_skia_util_ios.h', 'image/image_skia_util_ios.mm', @@ -229,6 +235,8 @@ 'paint_throbber.h', 'path.cc', 'path.h', + 'path_mac.h', + 'path_mac.mm', 'path_win.cc', 'path_win.h', 'path_x11.cc', @@ -267,8 +275,6 @@ 'screen_aura.cc', 'screen_ios.mm', 'screen_mac.mm', - 'screen_win.cc', - 'screen_win.h', 'scrollbar_size.cc', 'scrollbar_size.h', 'selection_model.cc', @@ -308,6 +314,10 @@ 'win/dpi.h', 'win/hwnd_util.cc', 'win/hwnd_util.h', + "win/physical_size.cc", + "win/physical_size.h", + 'win/rendering_window_manager.cc', + 'win/rendering_window_manager.h', 'win/scoped_set_map_mode.h', 'win/singleton_hwnd.cc', 'win/singleton_hwnd.h', @@ -519,6 +529,7 @@ 'image/image_unittest_util.h', 'image/image_unittest_util_ios.mm', 'image/image_unittest_util_mac.mm', + 'test/display_util.h', 'test/fontconfig_util_linux.cc', 'test/fontconfig_util_linux.h', 'test/gfx_util.cc', diff --git a/chromium/ui/gfx/gfx_tests.gyp b/chromium/ui/gfx/gfx_tests.gyp index 6c72699139f..d468e831882 100644 --- a/chromium/ui/gfx/gfx_tests.gyp +++ b/chromium/ui/gfx/gfx_tests.gyp @@ -63,13 +63,13 @@ 'image/image_util_unittest.cc', 'mac/coordinate_conversion_unittest.mm', 'nine_image_painter_unittest.cc', + 'path_mac_unittest.mm', 'platform_font_linux_unittest.cc', 'platform_font_mac_unittest.mm', 'range/range_mac_unittest.mm', 'range/range_unittest.cc', 'range/range_win_unittest.cc', 'render_text_unittest.cc', - 'screen_win_unittest.cc', 'sequential_id_generator_unittest.cc', 'shadow_value_unittest.cc', 'skbitmap_operations_unittest.cc', diff --git a/chromium/ui/gfx/gpu_memory_buffer.cc b/chromium/ui/gfx/gpu_memory_buffer.cc index 538611c81ea..fe6d4d192ac 100644 --- a/chromium/ui/gfx/gpu_memory_buffer.cc +++ b/chromium/ui/gfx/gpu_memory_buffer.cc @@ -20,6 +20,9 @@ GpuMemoryBufferHandle::GpuMemoryBufferHandle() : type(EMPTY_BUFFER), id(0), handle(base::SharedMemory::NULLHandle()) { } +GpuMemoryBufferHandle::GpuMemoryBufferHandle( + const GpuMemoryBufferHandle& other) = default; + GpuMemoryBufferHandle::~GpuMemoryBufferHandle() {} bool GpuMemoryBuffer::IsInUseByMacOSWindowServer() const { diff --git a/chromium/ui/gfx/gpu_memory_buffer.h b/chromium/ui/gfx/gpu_memory_buffer.h index 15ef3c4140d..423dcf617e7 100644 --- a/chromium/ui/gfx/gpu_memory_buffer.h +++ b/chromium/ui/gfx/gpu_memory_buffer.h @@ -18,7 +18,7 @@ #if defined(USE_OZONE) #include "ui/gfx/native_pixmap_handle_ozone.h" -#elif defined(OS_MACOSX) +#elif defined(OS_MACOSX) && !defined(OS_IOS) #include "ui/gfx/mac/io_surface.h" #endif @@ -39,6 +39,7 @@ using GpuMemoryBufferId = GenericSharedMemoryId; struct GFX_EXPORT GpuMemoryBufferHandle { GpuMemoryBufferHandle(); + GpuMemoryBufferHandle(const GpuMemoryBufferHandle& other); ~GpuMemoryBufferHandle(); bool is_null() const { return type == EMPTY_BUFFER; } GpuMemoryBufferType type; @@ -48,7 +49,7 @@ struct GFX_EXPORT GpuMemoryBufferHandle { int32_t stride; #if defined(USE_OZONE) NativePixmapHandle native_pixmap_handle; -#elif defined(OS_MACOSX) +#elif defined(OS_MACOSX) && !defined(OS_IOS) ScopedRefCountedIOSurfaceMachPort mach_port; #endif }; diff --git a/chromium/ui/gfx/harfbuzz_font_skia.cc b/chromium/ui/gfx/harfbuzz_font_skia.cc index 723b68aedc9..e2adeaff4d9 100644 --- a/chromium/ui/gfx/harfbuzz_font_skia.cc +++ b/chromium/ui/gfx/harfbuzz_font_skia.cc @@ -16,6 +16,7 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkTypeface.h" #include "ui/gfx/render_text.h" +#include "ui/gfx/skia_util.h" namespace gfx { @@ -65,14 +66,14 @@ void GetGlyphWidthAndExtents(SkPaint* paint, paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); if (width) - *width = SkScalarToFixed(sk_width); + *width = SkiaScalarToHarfBuzzUnits(sk_width); if (extents) { // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be // y-grows-up. - extents->x_bearing = SkScalarToFixed(sk_bounds.fLeft); - extents->y_bearing = SkScalarToFixed(-sk_bounds.fTop); - extents->width = SkScalarToFixed(sk_bounds.width()); - extents->height = SkScalarToFixed(-sk_bounds.height()); + extents->x_bearing = SkiaScalarToHarfBuzzUnits(sk_bounds.fLeft); + extents->y_bearing = SkiaScalarToHarfBuzzUnits(-sk_bounds.fTop); + extents->width = SkiaScalarToHarfBuzzUnits(sk_bounds.width()); + extents->height = SkiaScalarToHarfBuzzUnits(-sk_bounds.height()); } } @@ -132,7 +133,7 @@ hb_position_t GetGlyphKerning(FontData* font_data, SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); SkScalar size = font_data->paint_.getTextSize(); - return SkScalarToFixed( + return SkiaScalarToHarfBuzzUnits( SkScalarMulDiv(SkIntToScalar(kerning_adjustments[0]), size, upm)); } @@ -271,7 +272,7 @@ hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, face_cache->first.Init(skia_face); hb_font_t* harfbuzz_font = hb_font_create(face_cache->first.get()); - const int scale = SkScalarToFixed(text_size); + const int scale = SkiaScalarToHarfBuzzUnits(text_size); hb_font_set_scale(harfbuzz_font, scale, scale); FontData* hb_font_data = new FontData(&face_cache->second); hb_font_data->paint_.setTypeface(skia_face); diff --git a/chromium/ui/gfx/icon_util_unittest.cc b/chromium/ui/gfx/icon_util_unittest.cc index 7daa8cbf551..5ce5aa3055e 100644 --- a/chromium/ui/gfx/icon_util_unittest.cc +++ b/chromium/ui/gfx/icon_util_unittest.cc @@ -176,14 +176,14 @@ TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) { bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); bitmap->setInfo(SkImageInfo::MakeA8(kSmallIconWidth, kSmallIconHeight)); - icon = IconUtil::CreateHICONFromSkBitmap(*bitmap).Pass(); + icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); EXPECT_FALSE(icon.is_valid()); // Invalid bitmap size. bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); bitmap->setInfo(SkImageInfo::MakeN32Premul(0, 0)); - icon = IconUtil::CreateHICONFromSkBitmap(*bitmap).Pass(); + icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); EXPECT_FALSE(icon.is_valid()); // Valid bitmap configuration but no pixels allocated. @@ -191,7 +191,7 @@ TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) { ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); bitmap->setInfo(SkImageInfo::MakeN32Premul(kSmallIconWidth, kSmallIconHeight)); - icon = IconUtil::CreateHICONFromSkBitmap(*bitmap).Pass(); + icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); EXPECT_FALSE(icon.is_valid()); } diff --git a/chromium/ui/gfx/image/image.cc b/chromium/ui/gfx/image/image.cc index 9b59496a0a8..a306cc27418 100644 --- a/chromium/ui/gfx/image/image.cc +++ b/chromium/ui/gfx/image/image.cc @@ -510,8 +510,7 @@ const ImageSkia* Image::ToImageSkia() const { NOTREACHED(); } CHECK(scoped_rep); - rep = scoped_rep.get(); - AddRepresentation(std::move(scoped_rep)); + rep = AddRepresentation(std::move(scoped_rep)); } return rep->AsImageRepSkia()->image(); } @@ -541,8 +540,7 @@ UIImage* Image::ToUIImage() const { NOTREACHED(); } CHECK(scoped_rep); - rep = scoped_rep.get(); - AddRepresentation(std::move(scoped_rep)); + rep = AddRepresentation(std::move(scoped_rep)); } return rep->AsImageRepCocoaTouch()->image(); } @@ -575,8 +573,7 @@ NSImage* Image::ToNSImage() const { NOTREACHED(); } CHECK(scoped_rep); - rep = scoped_rep.get(); - AddRepresentation(std::move(scoped_rep)); + rep = AddRepresentation(std::move(scoped_rep)); } return rep->AsImageRepCocoa()->image(); } @@ -749,10 +746,18 @@ internal::ImageRep* Image::GetRepresentation( return it->second.get(); } -void Image::AddRepresentation(scoped_ptr<internal::ImageRep> rep) const { +internal::ImageRep* Image::AddRepresentation( + scoped_ptr<internal::ImageRep> rep) const { CHECK(storage_.get()); RepresentationType type = rep->type(); - storage_->representations().insert(std::make_pair(type, std::move(rep))); + 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(); } } // namespace gfx diff --git a/chromium/ui/gfx/image/image.h b/chromium/ui/gfx/image/image.h index ec05b26d8ff..fa7ed1f2695 100644 --- a/chromium/ui/gfx/image/image.h +++ b/chromium/ui/gfx/image/image.h @@ -183,8 +183,11 @@ class GFX_EXPORT Image { internal::ImageRep* GetRepresentation( RepresentationType rep_type, bool must_exist) const; - // Stores a representation into the map. - void AddRepresentation(scoped_ptr<internal::ImageRep> rep) 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( + scoped_ptr<internal::ImageRep> rep) const; // Internal class that holds all the representations. This allows the Image to // be cheaply copied. diff --git a/chromium/ui/gfx/image/image_png_rep.cc b/chromium/ui/gfx/image/image_png_rep.cc index 78b496fe95b..8ffa3e26ffe 100644 --- a/chromium/ui/gfx/image/image_png_rep.cc +++ b/chromium/ui/gfx/image/image_png_rep.cc @@ -22,6 +22,8 @@ ImagePNGRep::ImagePNGRep(const scoped_refptr<base::RefCountedMemory>& data, scale(data_scale) { } +ImagePNGRep::ImagePNGRep(const ImagePNGRep& other) = default; + ImagePNGRep::~ImagePNGRep() { } diff --git a/chromium/ui/gfx/image/image_png_rep.h b/chromium/ui/gfx/image/image_png_rep.h index 5c26134fa73..cdfe41d27ba 100644 --- a/chromium/ui/gfx/image/image_png_rep.h +++ b/chromium/ui/gfx/image/image_png_rep.h @@ -18,6 +18,7 @@ struct GFX_EXPORT ImagePNGRep { ImagePNGRep(); ImagePNGRep(const scoped_refptr<base::RefCountedMemory>& data, float data_scale); + ImagePNGRep(const ImagePNGRep& other); ~ImagePNGRep(); // Width and height of the image, in pixels. diff --git a/chromium/ui/gfx/image/image_skia.cc b/chromium/ui/gfx/image/image_skia.cc index b46d38d5ecb..8a1a2c99555 100644 --- a/chromium/ui/gfx/image/image_skia.cc +++ b/chromium/ui/gfx/image/image_skia.cc @@ -45,19 +45,6 @@ const float kFallbackToSmallerScaleDiff = 0.20f; namespace internal { namespace { -class Matcher { - public: - explicit Matcher(float scale) : scale_(scale) { - } - - bool operator()(const ImageSkiaRep& rep) const { - return rep.scale() == scale_; - } - - private: - float scale_; -}; - ImageSkiaRep ScaleImageSkiaRep(const ImageSkiaRep& rep, float target_scale) { if (rep.is_null() || rep.scale() == target_scale) return rep; @@ -76,75 +63,39 @@ ImageSkiaRep ScaleImageSkiaRep(const ImageSkiaRep& rep, float target_scale) { // A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a // refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's // information. Having both |base::RefCountedThreadSafe| and -// |base::NonThreadSafe| may sounds strange but necessary to turn +// |base::NonThreadSafe| may sound strange but is necessary to turn // the 'thread-non-safe modifiable ImageSkiaStorage' into // the 'thread-safe read-only ImageSkiaStorage'. class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, public base::NonThreadSafe { public: - ImageSkiaStorage(ImageSkiaSource* source, const gfx::Size& size) - : source_(source), - size_(size), - read_only_(false) { - } - - ImageSkiaStorage(ImageSkiaSource* source, float scale) - : source_(source), - read_only_(false) { - ImageSkia::ImageSkiaReps::iterator it = FindRepresentation(scale, true); - if (it == image_reps_.end() || it->is_null()) - source_.reset(); - else - size_.SetSize(it->GetWidth(), it->GetHeight()); - } - - bool has_source() const { return source_.get() != NULL; } + ImageSkiaStorage(ImageSkiaSource* source, const gfx::Size& size); + ImageSkiaStorage(ImageSkiaSource* source, float scale); + bool has_source() const { return source_ != nullptr; } std::vector<gfx::ImageSkiaRep>& image_reps() { return image_reps_; } - const gfx::Size& size() const { return size_; } - bool read_only() const { return read_only_; } - void DeleteSource() { - source_.reset(); - } - - void SetReadOnly() { - read_only_ = true; - } - - void DetachFromThread() { - base::NonThreadSafe::DetachFromThread(); - } + void DeleteSource(); + void SetReadOnly(); + void DetachFromThread(); // Checks if the current thread can safely modify the storage. - bool CanModify() const { - return !read_only_ && CalledOnValidThread(); - } + bool CanModify() const; // Checks if the current thread can safely read the storage. - bool CanRead() const { - return (read_only_ && !source_.get()) || CalledOnValidThread(); - } + bool CanRead() const; // Add a new representation. This checks if the scale of the added image // is not 1.0f, and mark the existing rep as scaled to make // the image high DPI aware. - void AddRepresentation(const ImageSkiaRep& image) { - if (image.scale() != 1.0f) { - for (ImageSkia::ImageSkiaReps::iterator it = image_reps_.begin(); - it < image_reps_.end(); - ++it) { - if (it->unscaled()) { - DCHECK_EQ(1.0f, it->scale()); - it->SetScaled(); - break; - } - } - } - image_reps_.push_back(image); - } + void AddRepresentation(const ImageSkiaRep& image); + + // Returns whether the underlying image source can provide a representation at + // any scale. In this case, the caller is guaranteed that + // FindRepresentation(..., true) will always succeed. + bool HasRepresentationAtAllScales() const; // Returns the iterator of the image rep whose density best matches // |scale|. If the image for the |scale| doesn't exist in the storage and @@ -159,91 +110,13 @@ class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, // Right now only Windows uses 2 and other platforms use 1 by default. // TODO(mukai, oshima): abandon 1 code path and use 2 for every platforms. std::vector<ImageSkiaRep>::iterator FindRepresentation( - float scale, bool fetch_new_image) const { - ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); - - ImageSkia::ImageSkiaReps::iterator closest_iter = - non_const->image_reps().end(); - ImageSkia::ImageSkiaReps::iterator exact_iter = - non_const->image_reps().end(); - float smallest_diff = std::numeric_limits<float>::max(); - for (ImageSkia::ImageSkiaReps::iterator it = - non_const->image_reps().begin(); - it < image_reps_.end(); ++it) { - if (it->scale() == scale) { - // found exact match - fetch_new_image = false; - if (it->is_null()) - continue; - exact_iter = it; - break; - } - float diff = std::abs(it->scale() - scale); - if (diff < smallest_diff && !it->is_null()) { - closest_iter = it; - smallest_diff = diff; - } - } - - if (fetch_new_image && source_.get()) { - DCHECK(CalledOnValidThread()) << - "An ImageSkia with the source must be accessed by the same thread."; - - ImageSkiaRep image; - float resource_scale = scale; - if (g_supported_scales) { - if (g_supported_scales->back() <= scale) { - resource_scale = g_supported_scales->back(); - } else { - for (size_t i = 0; i < g_supported_scales->size(); ++i) { - if ((*g_supported_scales)[i] + kFallbackToSmallerScaleDiff >= - scale) { - resource_scale = (*g_supported_scales)[i]; - break; - } - } - } - } - if (scale != resource_scale) { - std::vector<ImageSkiaRep>::iterator iter = FindRepresentation( - resource_scale, fetch_new_image); - DCHECK(iter != image_reps_.end()); - image = iter->unscaled() ? (*iter) : ScaleImageSkiaRep(*iter, scale); - } else { - image = source_->GetImageForScale(scale); - // Image may be missing for the specified scale in some cases, such like - // looking up 2x resources but the 2x resource pack is missing. Falls - // back to 1x and re-scale it. - if (image.is_null() && scale != 1.0f) - image = ScaleImageSkiaRep(source_->GetImageForScale(1.0f), scale); - } - - // If the source returned the new image, store it. - if (!image.is_null() && - std::find_if(image_reps_.begin(), image_reps_.end(), - Matcher(image.scale())) == image_reps_.end()) { - non_const->image_reps().push_back(image); - } - - // If the result image's scale isn't same as the expected scale, create - // null ImageSkiaRep with the |scale| so that the next lookup will - // fallback to the closest scale. - if (image.is_null() || image.scale() != scale) { - non_const->image_reps().push_back(ImageSkiaRep(SkBitmap(), scale)); - } - - // image_reps_ must have the exact much now, so find again. - return FindRepresentation(scale, false); - } - return exact_iter != image_reps_.end() ? exact_iter : closest_iter; - } + float scale, + bool fetch_new_image) const; private: - virtual ~ImageSkiaStorage() { - // We only care if the storage is modified by the same thread. - // Don't blow up even if someone else deleted the ImageSkia. - DetachFromThread(); - } + friend class base::RefCountedThreadSafe<ImageSkiaStorage>; + + virtual ~ImageSkiaStorage(); // Vector of bitmaps and their associated scale. std::vector<gfx::ImageSkiaRep> image_reps_; @@ -255,9 +128,150 @@ class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, bool read_only_; - friend class base::RefCountedThreadSafe<ImageSkiaStorage>; + DISALLOW_COPY_AND_ASSIGN(ImageSkiaStorage); }; +ImageSkiaStorage::ImageSkiaStorage(ImageSkiaSource* source, + const gfx::Size& size) + : source_(source), size_(size), read_only_(false) {} + +ImageSkiaStorage::ImageSkiaStorage(ImageSkiaSource* source, float scale) + : source_(source), read_only_(false) { + ImageSkia::ImageSkiaReps::iterator it = FindRepresentation(scale, true); + if (it == image_reps_.end() || it->is_null()) + source_.reset(); + else + size_.SetSize(it->GetWidth(), it->GetHeight()); +} + +void ImageSkiaStorage::DeleteSource() { + source_.reset(); +} + +void ImageSkiaStorage::SetReadOnly() { + read_only_ = true; +} + +void ImageSkiaStorage::DetachFromThread() { + base::NonThreadSafe::DetachFromThread(); +} + +bool ImageSkiaStorage::CanModify() const { + return !read_only_ && CalledOnValidThread(); +} + +bool ImageSkiaStorage::CanRead() const { + return (read_only_ && !source_) || CalledOnValidThread(); +} + +void ImageSkiaStorage::AddRepresentation(const ImageSkiaRep& image) { + // Explicitly adding a representation makes no sense for images that + // inherently have representations at all scales already. + DCHECK(!HasRepresentationAtAllScales()); + + if (image.scale() != 1.0f) { + for (ImageSkia::ImageSkiaReps::iterator it = image_reps_.begin(); + it < image_reps_.end(); ++it) { + if (it->unscaled()) { + DCHECK_EQ(1.0f, it->scale()); + it->SetScaled(); + break; + } + } + } + image_reps_.push_back(image); +} + +bool ImageSkiaStorage::HasRepresentationAtAllScales() const { + return source_ && source_->HasRepresentationAtAllScales(); +} + +std::vector<ImageSkiaRep>::iterator ImageSkiaStorage::FindRepresentation( + float scale, + bool fetch_new_image) const { + ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); + + ImageSkia::ImageSkiaReps::iterator closest_iter = + non_const->image_reps().end(); + ImageSkia::ImageSkiaReps::iterator exact_iter = non_const->image_reps().end(); + float smallest_diff = std::numeric_limits<float>::max(); + for (ImageSkia::ImageSkiaReps::iterator it = non_const->image_reps().begin(); + it < image_reps_.end(); ++it) { + if (it->scale() == scale) { + // found exact match + fetch_new_image = false; + if (it->is_null()) + continue; + exact_iter = it; + break; + } + float diff = std::abs(it->scale() - scale); + if (diff < smallest_diff && !it->is_null()) { + closest_iter = it; + smallest_diff = diff; + } + } + + if (fetch_new_image && source_.get()) { + DCHECK(CalledOnValidThread()) + << "An ImageSkia with the source must be accessed by the same thread."; + + ImageSkiaRep image; + float resource_scale = scale; + if (g_supported_scales) { + if (g_supported_scales->back() <= scale) { + resource_scale = g_supported_scales->back(); + } else { + for (size_t i = 0; i < g_supported_scales->size(); ++i) { + if ((*g_supported_scales)[i] + kFallbackToSmallerScaleDiff >= scale) { + resource_scale = (*g_supported_scales)[i]; + break; + } + } + } + } + if (scale != resource_scale) { + std::vector<ImageSkiaRep>::iterator iter = + FindRepresentation(resource_scale, fetch_new_image); + DCHECK(iter != image_reps_.end()); + image = iter->unscaled() ? (*iter) : ScaleImageSkiaRep(*iter, scale); + } else { + image = source_->GetImageForScale(scale); + // Image may be missing for the specified scale in some cases, such like + // looking up 2x resources but the 2x resource pack is missing. Fall back + // to 1x and re-scale it. + if (image.is_null() && scale != 1.0f) + image = ScaleImageSkiaRep(source_->GetImageForScale(1.0f), scale); + } + + // If the source returned the new image, store it. + if (!image.is_null() && + std::find_if(image_reps_.begin(), image_reps_.end(), + [&image](const ImageSkiaRep& rep) { + return rep.scale() == image.scale(); + }) == image_reps_.end()) { + non_const->image_reps().push_back(image); + } + + // If the result image's scale isn't same as the expected scale, create a + // null ImageSkiaRep with the |scale| so that the next lookup will fall back + // to the closest scale. + if (image.is_null() || image.scale() != scale) { + non_const->image_reps().push_back(ImageSkiaRep(SkBitmap(), scale)); + } + + // image_reps_ must have the exact much now, so find again. + return FindRepresentation(scale, false); + } + return exact_iter != image_reps_.end() ? exact_iter : closest_iter; +} + +ImageSkiaStorage::~ImageSkiaStorage() { + // We only care if the storage is modified by the same thread. Don't blow up + // even if someone else deleted the ImageSkia. + DetachFromThread(); +} + } // internal ImageSkia::ImageSkia() : storage_(NULL) { @@ -378,6 +392,14 @@ bool ImageSkia::HasRepresentation(float scale) const { return false; CHECK(CanRead()); + // This check is not only faster than FindRepresentation(), it's important for + // getting the right answer in cases of image types that are not based on + // discrete preset underlying representations, which otherwise might report + // "false" for this if GetRepresentation() has not yet been called for this + // |scale|. + if (storage_->HasRepresentationAtAllScales()) + return true; + ImageSkiaReps::iterator it = storage_->FindRepresentation(scale, false); return (it != storage_->image_reps().end() && it->scale() == scale); } diff --git a/chromium/ui/gfx/image/image_skia_source.cc b/chromium/ui/gfx/image/image_skia_source.cc new file mode 100644 index 00000000000..53bc4271ca7 --- /dev/null +++ b/chromium/ui/gfx/image/image_skia_source.cc @@ -0,0 +1,13 @@ +// 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/image/image_skia_source.h" + +namespace gfx { + +ImageSkiaSource::~ImageSkiaSource() {} + +bool ImageSkiaSource::HasRepresentationAtAllScales() const { return false; } + +} // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_source.h b/chromium/ui/gfx/image/image_skia_source.h index 6250032292b..9c50760edb6 100644 --- a/chromium/ui/gfx/image/image_skia_source.h +++ b/chromium/ui/gfx/image/image_skia_source.h @@ -15,13 +15,17 @@ class ImageSkiaRep; class GFX_EXPORT ImageSkiaSource { public: - virtual ~ImageSkiaSource() {} + virtual ~ImageSkiaSource(); // Returns the ImageSkiaRep for the given |scale|. ImageSkia caches the // returned ImageSkiaRep and calls this method only if it doesn't have // ImageSkiaRep for given |scale|. There is no need for the implementation to // cache the image. virtual gfx::ImageSkiaRep GetImageForScale(float scale) = 0; + + // Subclasses should override this to return true when they are capable of + // providing an exact representation at any desired scale factor. + virtual bool HasRepresentationAtAllScales() const; }; } // namespace gfx diff --git a/chromium/ui/gfx/image/image_unittest.cc b/chromium/ui/gfx/image/image_unittest.cc index d39adacd581..641ccfa88c7 100644 --- a/chromium/ui/gfx/image/image_unittest.cc +++ b/chromium/ui/gfx/image/image_unittest.cc @@ -269,7 +269,15 @@ TEST_F(ImageTest, MultiResolutionPNGToImageSkia) { #endif } -TEST_F(ImageTest, MultiResolutionPNGToPlatform) { +// TODO(crbug.com/153782): disable this test as it fails on iOS retina devices. +#if defined(OS_IOS) +#define MAYBE_MultiResolutionPNGToPlatform \ + DISABLED_MultiResolutionPNGToPlatform +#else +#define MAYBE_MultiResolutionPNGToPlatform \ + MultiResolutionPNGToPlatform +#endif +TEST_F(ImageTest, MAYBE_MultiResolutionPNGToPlatform) { const int kSize1x = 25; const int kSize2x = 50; @@ -319,7 +327,15 @@ TEST_F(ImageTest, PlatformToPNGEncodeAndDecode) { // The platform types use the platform provided encoding/decoding of PNGs. Make // sure these work with the Skia Encode/Decode. -TEST_F(ImageTest, PNGEncodeFromSkiaDecodeToPlatform) { +// TODO(crbug.com/153782): disable this test as it fails on iOS retina devices. +#if defined(OS_IOS) +#define MAYBE_PNGEncodeFromSkiaDecodeToPlatform \ + DISABLED_PNGEncodeFromSkiaDecodeToPlatform +#else +#define MAYBE_PNGEncodeFromSkiaDecodeToPlatform \ + PNGEncodeFromSkiaDecodeToPlatform +#endif +TEST_F(ImageTest, MAYBE_PNGEncodeFromSkiaDecodeToPlatform) { // Force the conversion sequence skia to png to platform_type. gfx::Image from_bitmap = gfx::Image::CreateFrom1xBitmap( gt::CreateBitmap(25, 25)); diff --git a/chromium/ui/gfx/image/image_unittest_util.cc b/chromium/ui/gfx/image/image_unittest_util.cc index 2e150a87701..182826f7e1f 100644 --- a/chromium/ui/gfx/image/image_unittest_util.cc +++ b/chromium/ui/gfx/image/image_unittest_util.cc @@ -211,10 +211,16 @@ bool IsEmpty(const gfx::Image& image) { } PlatformImage CreatePlatformImage() { - const SkBitmap bitmap(CreateBitmap(25, 25)); + SkBitmap bitmap(CreateBitmap(25, 25)); #if defined(OS_IOS) float scale = ImageSkia::GetMaxSupportedScale(); + if (scale > 1.0) { + // Always create a 25pt x 25pt image. + int size = static_cast<int>(25 * scale); + bitmap = CreateBitmap(size, size); + } + base::ScopedCFTypeRef<CGColorSpaceRef> color_space( CGColorSpaceCreateDeviceRGB()); UIImage* image = diff --git a/chromium/ui/gfx/image/image_util.cc b/chromium/ui/gfx/image/image_util.cc index e991da955e4..53bcceee073 100644 --- a/chromium/ui/gfx/image/image_util.cc +++ b/chromium/ui/gfx/image/image_util.cc @@ -15,9 +15,23 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia.h" -namespace gfx { +namespace { + +// Returns whether column |x| of |bitmap| has any "visible pixels", where +// "visible" is defined as having an opactiy greater than an arbitrary small +// value. +bool ColumnHasVisiblePixels(const SkBitmap& bitmap, int x) { + const SkAlpha kMinimumVisibleOpacity = 12; + for (int y = 0; y < bitmap.height(); ++y) { + if (SkColorGetA(bitmap.getColor(x, y)) > kMinimumVisibleOpacity) + return true; + } + return false; +} -const uint32_t kMinimumVisibleOpacity = 12; +} // namespace + +namespace gfx { // The iOS implementations of the JPEG functions are in image_util_ios.mm. #if !defined(OS_IOS) @@ -52,57 +66,39 @@ bool JPEG1xEncodedDataFromImage(const Image& image, int quality, } #endif // !defined(OS_IOS) -bool VisibleMargins(const ImageSkia& image, int* leading, int* trailing) { - *leading = 0; - *trailing = std::max(1, image.width()) - 1; - if (!image.HasRepresentation(1.0)) - return false; - - const ImageSkiaRep& rep = image.GetRepresentation(1.0); - if (rep.is_null()) - return false; - - const SkBitmap& bitmap = rep.sk_bitmap(); - if (bitmap.isNull() || bitmap.width() == 0) - return false; - - if (bitmap.isOpaque()) - return true; - - SkAutoLockPixels l(bitmap); - int inner_min = bitmap.width(); - for (int x = 0; x < bitmap.width(); ++x) { - for (int y = 0; y < bitmap.height(); ++y) { - if (SkColorGetA(bitmap.getColor(x, y)) > kMinimumVisibleOpacity) { - inner_min = x; - break; - } - } - if (inner_min < bitmap.width()) +void GetVisibleMargins(const ImageSkia& image, int* left, int* right) { + *left = 0; + *right = 0; + if (!image.HasRepresentation(1.f)) + return; + const SkBitmap& bitmap = image.GetRepresentation(1.f).sk_bitmap(); + if (bitmap.drawsNothing() || bitmap.isOpaque()) + return; + + SkAutoLockPixels lock(bitmap); + int x = 0; + for (; x < bitmap.width(); ++x) { + if (ColumnHasVisiblePixels(bitmap, x)) { + *left = x; break; - } - - int inner_max = -1; - for (int x = bitmap.width() - 1; x > inner_min; --x) { - for (int y = 0; y < bitmap.height(); ++y) { - if (SkColorGetA(bitmap.getColor(x, y)) > kMinimumVisibleOpacity) { - inner_max = x; - break; - } } - if (inner_max > -1) - break; } - if (inner_min == bitmap.width()) { - *leading = bitmap.width()/2; - *trailing = bitmap.width()/2 + 1; - return true; + if (x == bitmap.width()) { + // Image is fully transparent. Divide the width in half, giving the leading + // region the extra pixel for odd widths. + *left = (bitmap.width() + 1) / 2; + *right = bitmap.width() - *left; + return; } - *leading = inner_min; - *trailing = inner_max; - return true; + // Since we already know column *left is non-transparent, we can avoid + // rechecking that column; hence the '>' here. + for (x = bitmap.width() - 1; x > *left; --x) { + if (ColumnHasVisiblePixels(bitmap, x)) + break; + } + *right = bitmap.width() - 1 - x; } } // namespace gfx diff --git a/chromium/ui/gfx/image/image_util.h b/chromium/ui/gfx/image/image_util.h index c2ce2a105f6..8f17c368a8c 100644 --- a/chromium/ui/gfx/image/image_util.h +++ b/chromium/ui/gfx/image/image_util.h @@ -33,21 +33,15 @@ GFX_EXPORT bool JPEG1xEncodedDataFromImage(const Image& image, int quality, std::vector<unsigned char>* dst); -// Returns the visible (non-transparent) width of the 1x rep of the given -// image. If the image has no transparency, the leading value will be 0 and -// the trailing will be the image width. Return values are in the 1x width -// pixel units. Margins are given in 0-based column format. So if the image -// has only transparent pixels in columns 0, 1, 2, 3, then the leading value -// will be 4. Similarly, if there are all transparent pixels in column -// width-2, width-1, then the trailing margin value will be width-3. -// Returns true if the value is computed from opacity, false if it is a -// default value because of null image, missing Rep, etc. -// This method is only suitable for fairly small images (i.e. 16x16). -// The margins for a completely transparent image will be w/2-1, w/2, but this -// will be an expensive operation: it isn't expected that it will be frequently -// calculated. -GFX_EXPORT bool VisibleMargins(const ImageSkia& image, - int* leading, int* trailing); +// Computes the width of any nearly-transparent regions at the sides of the +// image and returns them in |left| and |right|. This checks each column of +// pixels from the outsides in, looking for anything with alpha above a +// reasonably small value. For a fully-opaque image, the margins will thus be +// (0, 0); for a fully-transparent image, the margins will be +// (width / 2, width / 2), with |left| getting the extra pixel for odd widths. +GFX_EXPORT void GetVisibleMargins(const ImageSkia& image, + int* left, + int* right); } // namespace gfx diff --git a/chromium/ui/gfx/image/image_util_unittest.cc b/chromium/ui/gfx/image/image_util_unittest.cc index 4816891cda1..2cde9c402b4 100644 --- a/chromium/ui/gfx/image/image_util_unittest.cc +++ b/chromium/ui/gfx/image/image_util_unittest.cc @@ -26,73 +26,110 @@ TEST(ImageUtilTest, JPEGEncodeAndDecode) { EXPECT_FALSE(decoded.IsEmpty()); } -TEST(ImageUtilTest, TestVisibleMargins) { - // Image with non-transparent piece should return margins at those - // columns. - SkBitmap bitmap1; - bitmap1.allocN32Pixels(16, 16); - bitmap1.eraseColor(SK_ColorTRANSPARENT); - bitmap1.eraseArea(SkIRect::MakeLTRB(3, 3, 14, 14), SK_ColorYELLOW); - gfx::ImageSkia img = gfx::ImageSkia::CreateFrom1xBitmap(bitmap1); - int x = 0; - int y = 0; - gfx::VisibleMargins(img, &x, &y); - EXPECT_EQ(3, x); - EXPECT_EQ(13, y); - EXPECT_EQ(16, img.width()); - - // Full-width-transparent image should return margins in the center - // of the image. - SkBitmap bitmap2; - bitmap2.allocN32Pixels(16, 16); - bitmap2.eraseColor(SK_ColorTRANSPARENT); - gfx::ImageSkia img_transparent = gfx::ImageSkia::CreateFrom1xBitmap(bitmap2); - x = 0; - y = 0; - gfx::VisibleMargins(img_transparent, &x, &y); - EXPECT_EQ(8, x); - EXPECT_EQ(9, y); - EXPECT_EQ(16, img_transparent.width()); - - // Image with non-transparent piece that is skewed to one side should - // return margins at those columns. - SkBitmap bitmap3; - bitmap3.allocN32Pixels(16, 16); - bitmap3.eraseColor(SK_ColorTRANSPARENT); - bitmap3.eraseArea(SkIRect::MakeLTRB(3, 3, 5, 5), SK_ColorYELLOW); - gfx::ImageSkia img3 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap3); - x = 0; - y = 0; - gfx::VisibleMargins(img3, &x, &y); - EXPECT_EQ(3, x); - EXPECT_EQ(4, y); - EXPECT_EQ(16, img3.width()); - - // Image with non-transparent piece that is at one edge should - // return margins at those columns. - SkBitmap bitmap4; - bitmap4.allocN32Pixels(16, 16); - bitmap4.eraseColor(SK_ColorTRANSPARENT); - bitmap4.eraseArea(SkIRect::MakeLTRB(0, 3, 5, 5), SK_ColorYELLOW); - gfx::ImageSkia img4 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap4); - x = 0; - y = 0; - gfx::VisibleMargins(img4, &x, &y); - EXPECT_EQ(0, x); - EXPECT_EQ(4, y); - EXPECT_EQ(16, img4.width()); - - // Image with non-transparent piece that is at trailing edge should - // return margins at those columns. - SkBitmap bitmap5; - bitmap5.allocN32Pixels(16, 16); - bitmap5.eraseColor(SK_ColorTRANSPARENT); - bitmap5.eraseArea(SkIRect::MakeLTRB(4, 3, 16, 16), SK_ColorYELLOW); - gfx::ImageSkia img5 = gfx::ImageSkia::CreateFrom1xBitmap(bitmap5); - x = 0; - y = 0; - gfx::VisibleMargins(img5, &x, &y); - EXPECT_EQ(4, x); - EXPECT_EQ(15, y); - EXPECT_EQ(16, img5.width()); +TEST(ImageUtilTest, GetVisibleMargins) { + int left, right; + + // Fully transparent image. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SK_ColorTRANSPARENT); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(8, left); + EXPECT_EQ(8, right); + } + + // Fully non-transparent image. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SK_ColorYELLOW); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(0, left); + EXPECT_EQ(0, right); + } + + // Image with non-transparent section in center. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SK_ColorTRANSPARENT); + bitmap.eraseArea(SkIRect::MakeLTRB(3, 2, 13, 13), SK_ColorYELLOW); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(3, left); + EXPECT_EQ(3, right); + } + + // Image with non-transparent section skewed to one side. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SK_ColorTRANSPARENT); + bitmap.eraseArea(SkIRect::MakeLTRB(3, 2, 5, 5), SK_ColorYELLOW); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(3, left); + EXPECT_EQ(11, right); + } + + // Image with non-transparent section at leading edge. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SK_ColorTRANSPARENT); + bitmap.eraseArea(SkIRect::MakeLTRB(0, 3, 5, 5), SK_ColorYELLOW); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(0, left); + EXPECT_EQ(11, right); + } + + // Image with non-transparent section at trailing edge. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SK_ColorTRANSPARENT); + bitmap.eraseArea(SkIRect::MakeLTRB(4, 3, 16, 13), SK_ColorYELLOW); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(4, left); + EXPECT_EQ(0, right); + } + + // Image with narrow non-transparent section. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SK_ColorTRANSPARENT); + bitmap.eraseArea(SkIRect::MakeLTRB(8, 3, 9, 5), SK_ColorYELLOW); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(8, left); + EXPECT_EQ(7, right); + } + + // Image with faint pixels. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 14); + bitmap.eraseColor(SkColorSetA(SK_ColorYELLOW, 0x02)); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(8, left); + EXPECT_EQ(8, right); + } + + // Fully transparent image with odd width. + { + SkBitmap bitmap; + bitmap.allocN32Pixels(17, 14); + bitmap.eraseColor(SK_ColorTRANSPARENT); + gfx::ImageSkia img(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + gfx::GetVisibleMargins(img, &left, &right); + EXPECT_EQ(9, left); + EXPECT_EQ(8, right); + } } diff --git a/chromium/ui/gfx/ipc/BUILD.gn b/chromium/ui/gfx/ipc/BUILD.gn index 28da1662635..b65ea3a7c3e 100644 --- a/chromium/ui/gfx/ipc/BUILD.gn +++ b/chromium/ui/gfx/ipc/BUILD.gn @@ -16,7 +16,6 @@ component("ipc") { deps = [ "//base", "//ipc", - "//skia", "//ui/gfx", "//ui/gfx/geometry", ] diff --git a/chromium/ui/gfx/ipc/gfx_ipc.gyp b/chromium/ui/gfx/ipc/gfx_ipc.gyp index 80e1947bea2..fac0e4c99d6 100644 --- a/chromium/ui/gfx/ipc/gfx_ipc.gyp +++ b/chromium/ui/gfx/ipc/gfx_ipc.gyp @@ -14,7 +14,6 @@ 'dependencies': [ '../../../base/base.gyp:base', '../../../ipc/ipc.gyp:ipc', - '../../../skia/skia.gyp:skia', '../gfx.gyp:gfx', '../gfx.gyp:gfx_geometry', ], diff --git a/chromium/ui/gfx/ipc/gfx_param_traits.cc b/chromium/ui/gfx/ipc/gfx_param_traits.cc index a49e1da9598..551957e095d 100644 --- a/chromium/ui/gfx/ipc/gfx_param_traits.cc +++ b/chromium/ui/gfx/ipc/gfx_param_traits.cc @@ -9,7 +9,6 @@ #include <string> -#include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" @@ -20,53 +19,14 @@ #include "ipc/mach_port_mac.h" #endif -namespace { - -struct SkBitmap_Data { - // The color type for the bitmap (bits per pixel, etc). - SkColorType fColorType; - - // The alpha type for the bitmap (opaque, premul, unpremul). - SkAlphaType fAlphaType; - - // The width of the bitmap in pixels. - uint32_t fWidth; - - // The height of the bitmap in pixels. - uint32_t fHeight; - - void InitSkBitmapDataForTransfer(const SkBitmap& bitmap) { - const SkImageInfo& info = bitmap.info(); - fColorType = info.colorType(); - fAlphaType = info.alphaType(); - fWidth = info.width(); - fHeight = info.height(); - } - - // Returns whether |bitmap| successfully initialized. - bool InitSkBitmapFromData(SkBitmap* bitmap, - const char* pixels, - size_t pixels_size) const { - if (!bitmap->tryAllocPixels( - SkImageInfo::Make(fWidth, fHeight, fColorType, fAlphaType))) - return false; - if (pixels_size != bitmap->getSize()) - return false; - memcpy(bitmap->getPixels(), pixels, pixels_size); - return true; - } -}; - -} // namespace - namespace IPC { -void ParamTraits<gfx::Point>::Write(Message* m, const gfx::Point& p) { +void ParamTraits<gfx::Point>::Write(base::Pickle* m, const gfx::Point& p) { WriteParam(m, p.x()); WriteParam(m, p.y()); } -bool ParamTraits<gfx::Point>::Read(const Message* m, +bool ParamTraits<gfx::Point>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::Point* r) { int x, y; @@ -81,12 +41,18 @@ void ParamTraits<gfx::Point>::Log(const gfx::Point& p, std::string* l) { l->append(base::StringPrintf("(%d, %d)", p.x(), p.y())); } -void ParamTraits<gfx::PointF>::Write(Message* m, const gfx::PointF& p) { +void ParamTraits<gfx::PointF>::GetSize(base::PickleSizer* s, + const gfx::PointF& p) { + GetParamSize(s, p.x()); + GetParamSize(s, p.y()); +} + +void ParamTraits<gfx::PointF>::Write(base::Pickle* m, const gfx::PointF& p) { WriteParam(m, p.x()); WriteParam(m, p.y()); } -bool ParamTraits<gfx::PointF>::Read(const Message* m, +bool ParamTraits<gfx::PointF>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::PointF* r) { float x, y; @@ -101,13 +67,13 @@ void ParamTraits<gfx::PointF>::Log(const gfx::PointF& p, std::string* l) { l->append(base::StringPrintf("(%f, %f)", p.x(), p.y())); } -void ParamTraits<gfx::Point3F>::Write(Message* m, const gfx::Point3F& p) { +void ParamTraits<gfx::Point3F>::Write(base::Pickle* m, const gfx::Point3F& p) { WriteParam(m, p.x()); WriteParam(m, p.y()); WriteParam(m, p.z()); } -bool ParamTraits<gfx::Point3F>::Read(const Message* m, +bool ParamTraits<gfx::Point3F>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::Point3F* r) { float x, y, z; @@ -124,14 +90,14 @@ void ParamTraits<gfx::Point3F>::Log(const gfx::Point3F& p, std::string* l) { l->append(base::StringPrintf("(%f, %f, %f)", p.x(), p.y(), p.z())); } -void ParamTraits<gfx::Size>::Write(Message* m, const gfx::Size& p) { +void ParamTraits<gfx::Size>::Write(base::Pickle* m, const gfx::Size& p) { DCHECK_GE(p.width(), 0); DCHECK_GE(p.height(), 0); int values[2] = { p.width(), p.height() }; m->WriteBytes(&values, sizeof(int) * 2); } -bool ParamTraits<gfx::Size>::Read(const Message* m, +bool ParamTraits<gfx::Size>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::Size* r) { const char* char_values; @@ -149,12 +115,12 @@ void ParamTraits<gfx::Size>::Log(const gfx::Size& p, std::string* l) { l->append(base::StringPrintf("(%d, %d)", p.width(), p.height())); } -void ParamTraits<gfx::SizeF>::Write(Message* m, const gfx::SizeF& p) { +void ParamTraits<gfx::SizeF>::Write(base::Pickle* m, const gfx::SizeF& p) { float values[2] = { p.width(), p.height() }; m->WriteBytes(&values, sizeof(float) * 2); } -bool ParamTraits<gfx::SizeF>::Read(const Message* m, +bool ParamTraits<gfx::SizeF>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::SizeF* r) { const char* char_values; @@ -170,12 +136,18 @@ void ParamTraits<gfx::SizeF>::Log(const gfx::SizeF& p, std::string* l) { l->append(base::StringPrintf("(%f, %f)", p.width(), p.height())); } -void ParamTraits<gfx::Vector2d>::Write(Message* m, const gfx::Vector2d& p) { +void ParamTraits<gfx::Vector2d>::GetSize(base::PickleSizer* s, + const gfx::Vector2d& p) { + s->AddBytes(sizeof(int) * 2); +} + +void ParamTraits<gfx::Vector2d>::Write(base::Pickle* m, + const gfx::Vector2d& p) { int values[2] = { p.x(), p.y() }; m->WriteBytes(&values, sizeof(int) * 2); } -bool ParamTraits<gfx::Vector2d>::Read(const Message* m, +bool ParamTraits<gfx::Vector2d>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::Vector2d* r) { const char* char_values; @@ -191,12 +163,13 @@ void ParamTraits<gfx::Vector2d>::Log(const gfx::Vector2d& v, std::string* l) { l->append(base::StringPrintf("(%d, %d)", v.x(), v.y())); } -void ParamTraits<gfx::Vector2dF>::Write(Message* m, const gfx::Vector2dF& p) { +void ParamTraits<gfx::Vector2dF>::Write(base::Pickle* m, + const gfx::Vector2dF& p) { float values[2] = { p.x(), p.y() }; m->WriteBytes(&values, sizeof(float) * 2); } -bool ParamTraits<gfx::Vector2dF>::Read(const Message* m, +bool ParamTraits<gfx::Vector2dF>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::Vector2dF* r) { const char* char_values; @@ -212,12 +185,12 @@ void ParamTraits<gfx::Vector2dF>::Log(const gfx::Vector2dF& v, std::string* l) { l->append(base::StringPrintf("(%f, %f)", v.x(), v.y())); } -void ParamTraits<gfx::Rect>::Write(Message* m, const gfx::Rect& p) { +void ParamTraits<gfx::Rect>::Write(base::Pickle* m, const gfx::Rect& p) { int values[4] = { p.x(), p.y(), p.width(), p.height() }; m->WriteBytes(&values, sizeof(int) * 4); } -bool ParamTraits<gfx::Rect>::Read(const Message* m, +bool ParamTraits<gfx::Rect>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::Rect* r) { const char* char_values; @@ -235,12 +208,17 @@ void ParamTraits<gfx::Rect>::Log(const gfx::Rect& p, std::string* l) { p.width(), p.height())); } -void ParamTraits<gfx::RectF>::Write(Message* m, const gfx::RectF& p) { +void ParamTraits<gfx::RectF>::GetSize(base::PickleSizer* s, + const gfx::RectF& p) { + s->AddBytes(sizeof(float) * 4); +} + +void ParamTraits<gfx::RectF>::Write(base::Pickle* m, const gfx::RectF& p) { float values[4] = { p.x(), p.y(), p.width(), p.height() }; m->WriteBytes(&values, sizeof(float) * 4); } -bool ParamTraits<gfx::RectF>::Read(const Message* m, +bool ParamTraits<gfx::RectF>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::RectF* r) { const char* char_values; @@ -256,57 +234,16 @@ void ParamTraits<gfx::RectF>::Log(const gfx::RectF& p, std::string* l) { p.width(), p.height())); } -void ParamTraits<SkBitmap>::Write(Message* m, const SkBitmap& p) { - size_t fixed_size = sizeof(SkBitmap_Data); - SkBitmap_Data bmp_data; - bmp_data.InitSkBitmapDataForTransfer(p); - m->WriteData(reinterpret_cast<const char*>(&bmp_data), - static_cast<int>(fixed_size)); - size_t pixel_size = p.getSize(); - SkAutoLockPixels p_lock(p); - m->WriteData(reinterpret_cast<const char*>(p.getPixels()), - static_cast<int>(pixel_size)); -} - -bool ParamTraits<SkBitmap>::Read(const Message* m, - base::PickleIterator* iter, - SkBitmap* r) { - const char* fixed_data; - int fixed_data_size = 0; - if (!iter->ReadData(&fixed_data, &fixed_data_size) || - (fixed_data_size <= 0)) { - NOTREACHED(); - return false; - } - if (fixed_data_size != sizeof(SkBitmap_Data)) - return false; // Message is malformed. - - const char* variable_data; - int variable_data_size = 0; - if (!iter->ReadData(&variable_data, &variable_data_size) || - (variable_data_size < 0)) { - NOTREACHED(); - return false; - } - const SkBitmap_Data* bmp_data = - reinterpret_cast<const SkBitmap_Data*>(fixed_data); - return bmp_data->InitSkBitmapFromData(r, variable_data, variable_data_size); -} - -void ParamTraits<SkBitmap>::Log(const SkBitmap& p, std::string* l) { - l->append("<SkBitmap>"); -} - -void ParamTraits<gfx::Range>::Write(Message* m, const gfx::Range& r) { - m->WriteSizeT(r.start()); - m->WriteSizeT(r.end()); +void ParamTraits<gfx::Range>::Write(base::Pickle* m, const gfx::Range& r) { + m->WriteUInt32(r.start()); + m->WriteUInt32(r.end()); } -bool ParamTraits<gfx::Range>::Read(const Message* m, +bool ParamTraits<gfx::Range>::Read(const base::Pickle* m, base::PickleIterator* iter, gfx::Range* r) { - size_t start, end; - if (!iter->ReadSizeT(&start) || !iter->ReadSizeT(&end)) + uint32_t start, end; + if (!iter->ReadUInt32(&start) || !iter->ReadUInt32(&end)) return false; r->set_start(start); r->set_end(end); @@ -314,15 +251,16 @@ bool ParamTraits<gfx::Range>::Read(const Message* m, } void ParamTraits<gfx::Range>::Log(const gfx::Range& r, std::string* l) { - l->append(base::StringPrintf("(%" PRIuS ", %" PRIuS ")", r.start(), r.end())); + l->append(base::StringPrintf("(%d, %d)", r.start(), r.end())); } -void ParamTraits<gfx::ScrollOffset>::Write(Message* m, const param_type& p) { +void ParamTraits<gfx::ScrollOffset>::Write(base::Pickle* m, + const param_type& p) { m->WriteDouble(p.x()); m->WriteDouble(p.y()); } -bool ParamTraits<gfx::ScrollOffset>::Read(const Message* m, +bool ParamTraits<gfx::ScrollOffset>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { double x = 0.f; @@ -346,14 +284,14 @@ void ParamTraits<gfx::ScrollOffset>::Log(const param_type& p, std::string* l) { #if defined(OS_MACOSX) && !defined(OS_IOS) void ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort>::Write( - Message* m, + base::Pickle* m, const param_type p) { MachPortMac mach_port_mac(p.get()); ParamTraits<MachPortMac>::Write(m, mach_port_mac); } bool ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort>::Read( - const Message* m, + const base::Pickle* m, base::PickleIterator* iter, param_type* r) { MachPortMac mach_port_mac; diff --git a/chromium/ui/gfx/ipc/gfx_param_traits.h b/chromium/ui/gfx/ipc/gfx_param_traits.h index b7280ef0510..5fbf8859224 100644 --- a/chromium/ui/gfx/ipc/gfx_param_traits.h +++ b/chromium/ui/gfx/ipc/gfx_param_traits.h @@ -17,8 +17,6 @@ #include "ui/gfx/mac/io_surface.h" #endif -class SkBitmap; - namespace gfx { class Point; class PointF; @@ -38,100 +36,113 @@ namespace IPC { template <> struct GFX_IPC_EXPORT ParamTraits<gfx::Point> { typedef gfx::Point param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::PointF> { typedef gfx::PointF param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void GetSize(base::PickleSizer* s, const param_type& p); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::Point3F> { typedef gfx::Point3F param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::Size> { typedef gfx::Size param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::SizeF> { typedef gfx::SizeF param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::Vector2d> { typedef gfx::Vector2d param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void GetSize(base::PickleSizer* s, const param_type& p); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::Vector2dF> { typedef gfx::Vector2dF param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::Rect> { typedef gfx::Rect param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::RectF> { typedef gfx::RectF param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct GFX_IPC_EXPORT ParamTraits<SkBitmap> { - typedef SkBitmap param_type; - static void Write(Message* m, const param_type& p); - - // Note: This function expects parameter |r| to be of type &SkBitmap since - // r->SetConfig() and r->SetPixels() are called. - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); - + static void GetSize(base::PickleSizer* s, const param_type& p); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::Range> { typedef gfx::Range param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; template <> struct GFX_IPC_EXPORT ParamTraits<gfx::ScrollOffset> { typedef gfx::ScrollOffset param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; @@ -139,12 +150,14 @@ struct GFX_IPC_EXPORT ParamTraits<gfx::ScrollOffset> { template <> struct GFX_IPC_EXPORT ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort> { typedef gfx::ScopedRefCountedIOSurfaceMachPort param_type; - static void Write(Message* m, const param_type p); + static void Write(base::Pickle* m, const param_type p); // Note: Read() passes ownership of the Mach send right from the IPC message // to the ScopedRefCountedIOSurfaceMachPort. Therefore, Read() may only be // called once for a given message, otherwise the singular right will be // managed and released by two objects. - static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); static void Log(const param_type& p, std::string* l); }; #endif // defined(OS_MACOSX) && !defined(OS_IOS) diff --git a/chromium/ui/gfx/ipc/skia/BUILD.gn b/chromium/ui/gfx/ipc/skia/BUILD.gn new file mode 100644 index 00000000000..60718d2eeae --- /dev/null +++ b/chromium/ui/gfx/ipc/skia/BUILD.gn @@ -0,0 +1,23 @@ +# 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. + +component("skia") { + output_name = "gfx_ipc_skia" + + sources = [ + "gfx_skia_param_traits.cc", + "gfx_skia_param_traits.h", + ] + + defines = [ "GFX_SKIA_IPC_IMPLEMENTATION" ] + + deps = [ + "//base", + "//ipc", + "//skia", + "//ui/gfx", + "//ui/gfx/geometry", + "//ui/gfx/ipc", + ] +} diff --git a/chromium/ui/gfx/ipc/skia/gfx_ipc_skia.gyp b/chromium/ui/gfx/ipc/skia/gfx_ipc_skia.gyp new file mode 100644 index 00000000000..337858def71 --- /dev/null +++ b/chromium/ui/gfx/ipc/skia/gfx_ipc_skia.gyp @@ -0,0 +1,34 @@ +# 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + # GN version: //ui/gfx/ipc/skia + 'target_name': 'gfx_ipc_skia', + 'type': '<(component)', + 'dependencies': [ + '../../../../base/base.gyp:base', + '../../../../ipc/ipc.gyp:ipc', + '../../../../skia/skia.gyp:skia', + '../../gfx.gyp:gfx', + '../../gfx.gyp:gfx_geometry', + '../gfx_ipc.gyp:gfx_ipc', + ], + 'defines': [ + 'GFX_SKIA_IPC_IMPLEMENTATION', + ], + 'include_dirs': [ + '../../../..', + ], + 'sources': [ + 'gfx_skia_param_traits.cc', + 'gfx_skia_param_traits.h', + ], + }, + ], +} diff --git a/chromium/ui/gfx/ipc/skia/gfx_skia_ipc_export.h b/chromium/ui/gfx/ipc/skia/gfx_skia_ipc_export.h new file mode 100644 index 00000000000..dc29fb945d6 --- /dev/null +++ b/chromium/ui/gfx/ipc/skia/gfx_skia_ipc_export.h @@ -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. + +#ifndef UI_GFX_IPC_GFX_SKIA_IPC_EXPORT_H_ +#define UI_GFX_IPC_GFX_SKIA_IPC_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(GFX_SKIA_IPC_IMPLEMENTATION) +#define GFX_SKIA_IPC_EXPORT __declspec(dllexport) +#else +#define GFX_SKIA_IPC_EXPORT __declspec(dllimport) +#endif // defined(GFX_SKIA_IPC_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(GFX_SKIA_IPC_IMPLEMENTATION) +#define GFX_SKIA_IPC_EXPORT __attribute__((visibility("default"))) +#else +#define GFX_SKIA_IPC_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define GFX_SKIA_IPC_EXPORT +#endif + +#endif // UI_GFX_IPC_GFX_SKIA_IPC_EXPORT_H_ diff --git a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc new file mode 100644 index 00000000000..033590c4060 --- /dev/null +++ b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc @@ -0,0 +1,135 @@ +// 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/ipc/skia/gfx_skia_param_traits.h" + +#include <string> + +#include "base/pickle.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "ui/gfx/transform.h" + +namespace { + +struct SkBitmap_Data { + // The color type for the bitmap (bits per pixel, etc). + SkColorType color_type; + + // The alpha type for the bitmap (opaque, premul, unpremul). + SkAlphaType alpha_type; + + // The width of the bitmap in pixels. + uint32_t width; + + // The height of the bitmap in pixels. + uint32_t height; + + void InitSkBitmapDataForTransfer(const SkBitmap& bitmap) { + const SkImageInfo& info = bitmap.info(); + color_type = info.colorType(); + alpha_type = info.alphaType(); + width = info.width(); + height = info.height(); + } + + // Returns whether |bitmap| successfully initialized. + bool InitSkBitmapFromData(SkBitmap* bitmap, + const char* pixels, + size_t pixels_size) const { + if (!bitmap->tryAllocPixels( + SkImageInfo::Make(width, height, color_type, alpha_type))) + return false; + if (pixels_size != bitmap->getSize()) + return false; + memcpy(bitmap->getPixels(), pixels, pixels_size); + return true; + } +}; + +} // namespace + +namespace IPC { + +void ParamTraits<SkBitmap>::Write(base::Pickle* m, const SkBitmap& p) { + size_t fixed_size = sizeof(SkBitmap_Data); + SkBitmap_Data bmp_data; + bmp_data.InitSkBitmapDataForTransfer(p); + m->WriteData(reinterpret_cast<const char*>(&bmp_data), + static_cast<int>(fixed_size)); + size_t pixel_size = p.getSize(); + SkAutoLockPixels p_lock(p); + m->WriteData(reinterpret_cast<const char*>(p.getPixels()), + static_cast<int>(pixel_size)); +} + +bool ParamTraits<SkBitmap>::Read(const base::Pickle* m, + base::PickleIterator* iter, + SkBitmap* r) { + const char* fixed_data; + int fixed_data_size = 0; + if (!iter->ReadData(&fixed_data, &fixed_data_size) || + (fixed_data_size <= 0)) { + return false; + } + if (fixed_data_size != sizeof(SkBitmap_Data)) + return false; // Message is malformed. + + const char* variable_data; + int variable_data_size = 0; + if (!iter->ReadData(&variable_data, &variable_data_size) || + (variable_data_size < 0)) { + return false; + } + const SkBitmap_Data* bmp_data = + reinterpret_cast<const SkBitmap_Data*>(fixed_data); + return bmp_data->InitSkBitmapFromData(r, variable_data, variable_data_size); +} + +void ParamTraits<SkBitmap>::Log(const SkBitmap& p, std::string* l) { + l->append("<SkBitmap>"); +} + +void ParamTraits<gfx::Transform>::Write(base::Pickle* m, const param_type& p) { +#ifdef SK_MSCALAR_IS_FLOAT + float column_major_data[16]; + p.matrix().asColMajorf(column_major_data); +#else + double column_major_data[16]; + p.matrix().asColMajord(column_major_data); +#endif + // We do this in a single write for performance reasons. + m->WriteBytes(&column_major_data, sizeof(SkMScalar) * 16); +} + +bool ParamTraits<gfx::Transform>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r) { + const char* column_major_data; + if (!iter->ReadBytes(&column_major_data, sizeof(SkMScalar) * 16)) + return false; + r->matrix().setColMajor( + reinterpret_cast<const SkMScalar*>(column_major_data)); + return true; +} + +void ParamTraits<gfx::Transform>::Log( + const param_type& p, std::string* l) { +#ifdef SK_MSCALAR_IS_FLOAT + float row_major_data[16]; + p.matrix().asRowMajorf(row_major_data); +#else + double row_major_data[16]; + p.matrix().asRowMajord(row_major_data); +#endif + l->append("("); + for (int i = 0; i < 16; ++i) { + if (i > 0) + l->append(", "); + LogParam(row_major_data[i], l); + } + l->append(") "); +} + +} // namespace IPC diff --git a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h new file mode 100644 index 00000000000..d3acc930865 --- /dev/null +++ b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h @@ -0,0 +1,49 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_IPC_GFX_SKIA_PARAM_TRAITS_H_ +#define UI_GFX_IPC_GFX_SKIA_PARAM_TRAITS_H_ + +#include <string> + +#include "ipc/ipc_message_utils.h" +#include "ipc/ipc_param_traits.h" +#include "ui/gfx/ipc/skia/gfx_skia_ipc_export.h" + +class SkBitmap; + +namespace base { +class Pickle; +class PickleIterator; +} + +namespace gfx { +class Transform; +} + +namespace IPC { + +template <> +struct GFX_SKIA_IPC_EXPORT ParamTraits<SkBitmap> { + using param_type = SkBitmap; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct GFX_SKIA_IPC_EXPORT ParamTraits<gfx::Transform> { + using param_type = gfx::Transform; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +} // namespace IPC + +#endif // UI_GFX_IPC_GFX_SKIA_PARAM_TRAITS_H_ diff --git a/chromium/ui/gfx/mac/io_surface.cc b/chromium/ui/gfx/mac/io_surface.cc index e48a813ad42..641c2e0470f 100644 --- a/chromium/ui/gfx/mac/io_surface.cc +++ b/chromium/ui/gfx/mac/io_surface.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include <stdint.h> +#include "base/feature_list.h" #include "base/logging.h" #include "base/mac/mac_util.h" #include "base/mac/mach_logging.h" @@ -17,6 +18,9 @@ namespace gfx { namespace { +const base::Feature kIOSurfaceClearYosemite{"IOSurfaceClearYosemite", + base::FEATURE_ENABLED_BY_DEFAULT}; + void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value) { @@ -155,7 +159,11 @@ IOSurfaceRef CreateIOSurface(const gfx::Size& size, gfx::BufferFormat format) { // causes PDFs to render incorrectly. Hopefully this check can be removed once // pdfium switches to a Skia backend on Mac. // https://crbug.com/594343. - if (!base::mac::IsOSMavericks()) { + bool should_clear = !base::mac::IsOSMavericks(); + if (base::mac::IsOSYosemite()) + should_clear = base::FeatureList::IsEnabled(kIOSurfaceClearYosemite); + + if (should_clear) { // Zero-initialize the IOSurface. Calling IOSurfaceLock/IOSurfaceUnlock // appears to be sufficient. https://crbug.com/584760#c17 IOReturn r = IOSurfaceLock(surface, 0, nullptr); diff --git a/chromium/ui/gfx/mac/io_surface.h b/chromium/ui/gfx/mac/io_surface.h index 4c24fe4697c..1fb01a060c0 100644 --- a/chromium/ui/gfx/mac/io_surface.h +++ b/chromium/ui/gfx/mac/io_surface.h @@ -24,6 +24,19 @@ struct IOSurfaceMachPortTraits { GFX_EXPORT static void Release(mach_port_t); }; +struct ScopedInUseIOSurfaceTraits { + static IOSurfaceRef InvalidValue() { return nullptr; } + static IOSurfaceRef Retain(IOSurfaceRef io_surface) { + CFRetain(io_surface); + IOSurfaceIncrementUseCount(io_surface); + return io_surface; + } + static void Release(IOSurfaceRef io_surface) { + IOSurfaceDecrementUseCount(io_surface); + CFRelease(io_surface); + } +}; + } // namespace internal using IOSurfaceId = GenericSharedMemoryId; @@ -38,6 +51,11 @@ GFX_EXPORT IOSurfaceRef CreateIOSurface(const Size& size, BufferFormat format); using ScopedRefCountedIOSurfaceMachPort = base::ScopedTypeRef<mach_port_t, internal::IOSurfaceMachPortTraits>; +// A scoper for holding a reference to an IOSurface and also incrementing its +// in-use counter while the scoper exists. +using ScopedInUseIOSurface = + base::ScopedTypeRef<IOSurfaceRef, internal::ScopedInUseIOSurfaceTraits>; + } // namespace gfx #endif // UI_GFX_MAC_IO_SURFACE_H_ diff --git a/chromium/ui/gfx/native_widget_types.h b/chromium/ui/gfx/native_widget_types.h index 8297630216c..d9db495b84d 100644 --- a/chromium/ui/gfx/native_widget_types.h +++ b/chromium/ui/gfx/native_widget_types.h @@ -9,7 +9,6 @@ #include "base/logging.h" #include "build/build_config.h" -#include "ui/gfx/gfx_export.h" #if defined(OS_ANDROID) #include <jni.h> @@ -213,29 +212,6 @@ typedef intptr_t NativeViewId; const PluginWindowHandle kNullPluginWindow = 0; #endif -enum SurfaceType { - EMPTY, - NATIVE_DIRECT, - NULL_TRANSPORT, - SURFACE_TYPE_LAST = NULL_TRANSPORT -}; - -struct GLSurfaceHandle { - GLSurfaceHandle() : handle(kNullPluginWindow), transport_type(EMPTY) {} - GLSurfaceHandle(PluginWindowHandle handle_, SurfaceType transport_) - : handle(handle_), transport_type(transport_) { - DCHECK(!is_null() || handle == kNullPluginWindow); - DCHECK(transport_type != NULL_TRANSPORT || - handle == kNullPluginWindow); - } - bool is_null() const { return transport_type == EMPTY; } - bool is_transport() const { - return transport_type == NULL_TRANSPORT; - } - PluginWindowHandle handle; - SurfaceType transport_type; -}; - // AcceleratedWidget provides a surface to compositors to paint pixels. #if defined(OS_WIN) typedef HWND AcceleratedWidget; diff --git a/chromium/ui/gfx/paint_vector_icon.cc b/chromium/ui/gfx/paint_vector_icon.cc index 7121fde314d..6091897f811 100644 --- a/chromium/ui/gfx/paint_vector_icon.cc +++ b/chromium/ui/gfx/paint_vector_icon.cc @@ -309,6 +309,10 @@ class VectorIconSource : public CanvasImageSource { ~VectorIconSource() override {} // CanvasImageSource: + bool HasRepresentationAtAllScales() const override { + return id_ != VectorIconId::VECTOR_ICON_NONE; + } + void Draw(gfx::Canvas* canvas) override { if (path_.empty()) { PaintVectorIcon(canvas, id_, size_.width(), color_); diff --git a/chromium/ui/gfx/path.h b/chromium/ui/gfx/path.h index bb00e13a365..e1cbfa9b93b 100644 --- a/chromium/ui/gfx/path.h +++ b/chromium/ui/gfx/path.h @@ -33,9 +33,6 @@ class GFX_EXPORT Path : public SkPath { Path(const PointF* points, size_t count); ~Path(); - - private: - DISALLOW_COPY_AND_ASSIGN(Path); }; } diff --git a/chromium/ui/gfx/path_mac.h b/chromium/ui/gfx/path_mac.h new file mode 100644 index 00000000000..f20fa62d7c2 --- /dev/null +++ b/chromium/ui/gfx/path_mac.h @@ -0,0 +1,21 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_PATH_MAC_H_ +#define UI_GFX_PATH_MAC_H_ + +#include "ui/gfx/gfx_export.h" + +@class NSBezierPath; +class SkPath; + +namespace gfx { + +// Returns an autoreleased NSBezierPath corresponding to |path|. Caller should +// call retain on the returned object, if it wishes to take ownership. +GFX_EXPORT NSBezierPath* CreateNSBezierPathFromSkPath(const SkPath& path); + +} // namespace gfx + +#endif // UI_GFX_PATH_MAC_H_ diff --git a/chromium/ui/gfx/path_mac.mm b/chromium/ui/gfx/path_mac.mm new file mode 100644 index 00000000000..109f80741f4 --- /dev/null +++ b/chromium/ui/gfx/path_mac.mm @@ -0,0 +1,125 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ui/gfx/path_mac.h" + +#import <Cocoa/Cocoa.h> + +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/gfx/path.h" + +namespace { + +// Convert a quadratic bezier curve to a cubic bezier curve. Based on the +// implementation of the private SkConvertQuadToCubic method inside Skia. +void ConvertQuadToCubicBezier(NSPoint quad[3], NSPoint cubic[4]) { + // The resultant cubic will have the same endpoints. + cubic[0] = quad[0]; + cubic[3] = quad[2]; + + const double scale = 2.0 / 3.0; + + cubic[1].x = quad[0].x + scale * (quad[1].x - quad[0].x); + cubic[1].y = quad[0].y + scale * (quad[1].y - quad[0].y); + + cubic[2].x = quad[2].x + scale * (quad[1].x - quad[2].x); + cubic[2].y = quad[2].y + scale * (quad[1].y - quad[2].y); +} + +} // namespace + +namespace gfx { + +NSBezierPath* CreateNSBezierPathFromSkPath(const SkPath& path) { + NSBezierPath* result = [NSBezierPath bezierPath]; + SkPath::RawIter iter(path); + SkPoint sk_points[4] = {{0.0}}; + SkPath::Verb verb; + NSPoint points[4]; + while ((verb = iter.next(sk_points)) != SkPath::kDone_Verb) { + for (size_t i = 0; i < arraysize(points); i++) + points[i] = NSMakePoint(sk_points[i].x(), sk_points[i].y()); + + switch (verb) { + case SkPath::kMove_Verb: { + [result moveToPoint:points[0]]; + break; + } + case SkPath::kLine_Verb: { + DCHECK(NSEqualPoints([result currentPoint], points[0])); + [result lineToPoint:points[1]]; + break; + } + case SkPath::kQuad_Verb: { + DCHECK(NSEqualPoints([result currentPoint], points[0])); + NSPoint quad[] = {points[0], points[1], points[2]}; + // NSBezierPath does not support quadratic bezier curves. Hence convert + // to cubic bezier curve. + ConvertQuadToCubicBezier(quad, points); + [result curveToPoint:points[3] + controlPoint1:points[1] + controlPoint2:points[2]]; + break; + } + case SkPath::kConic_Verb: { + DCHECK(NSEqualPoints([result currentPoint], points[0])); + // Approximate with quads. Use two for now, increase if more precision + // is needed. + const size_t kSubdivisionLevels = 1; + const size_t kQuadCount = 1 << kSubdivisionLevels; + // The quads will share endpoints, so we need one more point than twice + // the number of quads. + const size_t kPointCount = 1 + 2 * kQuadCount; + SkPoint quads[kPointCount]; + SkPath::ConvertConicToQuads(sk_points[0], sk_points[1], sk_points[2], + iter.conicWeight(), quads, + kSubdivisionLevels); + NSPoint ns_quads[kPointCount]; + for (size_t i = 0; i < kPointCount; i++) + ns_quads[i] = NSMakePoint(quads[i].x(), quads[i].y()); + + for (size_t i = 0; i < kQuadCount; i++) { + NSPoint quad[] = {ns_quads[2 * i], ns_quads[2 * i + 1], + ns_quads[2 * i + 2]}; + ConvertQuadToCubicBezier(quad, points); + DCHECK(NSEqualPoints([result currentPoint], points[0])); + [result curveToPoint:points[3] + controlPoint1:points[1] + controlPoint2:points[2]]; + } + break; + } + case SkPath::kCubic_Verb: { + DCHECK(NSEqualPoints([result currentPoint], points[0])); + [result curveToPoint:points[3] + controlPoint1:points[1] + controlPoint2:points[2]]; + break; + } + case SkPath::kClose_Verb: { + [result closePath]; + break; + } + default: { NOTREACHED(); } + } + } + + // Set up the fill type. + switch (path.getFillType()) { + case SkPath::kWinding_FillType: + [result setWindingRule:NSNonZeroWindingRule]; + break; + case SkPath::kEvenOdd_FillType: + [result setWindingRule:NSEvenOddWindingRule]; + break; + case SkPath::kInverseWinding_FillType: + case SkPath::kInverseEvenOdd_FillType: + NOTREACHED() << "NSBezierCurve does not support inverse fill types."; + break; + } + + return result; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/path_mac_unittest.mm b/chromium/ui/gfx/path_mac_unittest.mm new file mode 100644 index 00000000000..d1d4a99234e --- /dev/null +++ b/chromium/ui/gfx/path_mac_unittest.mm @@ -0,0 +1,258 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ui/gfx/path_mac.h" + +#include <cmath> +#include <vector> + +#import <Cocoa/Cocoa.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/path.h" + +namespace gfx { + +namespace { + +// Returns the point at a distance of |radius| from the point (|centre_x|, +// |centre_y|), and angle |degrees| from the positive horizontal axis, measured +// anti-clockwise. +NSPoint GetRadialPoint(double radius, + double degrees, + double centre_x, + double centre_y) { + const double radian = (degrees * SK_ScalarPI) / 180; + return NSMakePoint(centre_x + radius * std::cos(radian), + centre_y + radius * std::sin(radian)); +} + +// Returns the area of a circle with the given |radius|. +double CalculateCircleArea(double radius) { + return SK_ScalarPI * radius * radius; +} + +// Returns the area of a simple polygon. |path| should represent a simple +// polygon. +double CalculatePolygonArea(NSBezierPath* path) { + // If path represents a single polygon, it will have MoveTo, followed by + // multiple LineTo, followed By ClosePath, followed by another MoveTo + // NSBezierPathElement. + const size_t element_count = [path elementCount]; + NSPoint points[3]; + std::vector<NSPoint> poly; + + for (size_t i = 0; i < element_count - 1; i++) { + NSBezierPathElement element = + [path elementAtIndex:i associatedPoints:points]; + poly.push_back(points[0]); + DCHECK_EQ(element, + i ? (i == element_count - 2 ? NSClosePathBezierPathElement + : NSLineToBezierPathElement) + : NSMoveToBezierPathElement); + } + DCHECK_EQ([path elementAtIndex:element_count - 1], NSMoveToBezierPathElement); + + // Shoelace Algorithm to find the area of a simple polygon. + DCHECK(NSEqualPoints(poly.front(), poly.back())); + double area = 0; + for (size_t i = 0; i < poly.size() - 1; i++) + area += poly[i].x * poly[i + 1].y - poly[i].y * poly[i + 1].x; + + return std::fabs(area) / 2.0; +} + +// Returns the area of a rounded rectangle with the given |width|, |height| and +// |radius|. +double CalculateRoundedRectangleArea(double width, + double height, + double radius) { + const double inside_width = width - 2 * radius; + const double inside_height = height - 2 * radius; + return inside_width * inside_height + + 2 * radius * (inside_width + inside_height) + + CalculateCircleArea(radius); +} + +// Returns the bounding box of |path| as a Rect. +Rect GetBoundingBox(NSBezierPath* path) { + const NSRect bounds = [path bounds]; + return ToNearestRect(RectF(bounds.origin.x, bounds.origin.y, + bounds.size.width, bounds.size.height)); +} + +} // namespace + +// Check that empty NSBezierPath is returned for empty SkPath. +TEST(CreateNSBezierPathFromSkPathTest, EmptyPath) { + NSBezierPath* result = CreateNSBezierPathFromSkPath(SkPath()); + EXPECT_TRUE([result isEmpty]); +} + +// Check that the returned NSBezierPath has the correct winding rule. +TEST(CreateNSBezierPathFromSkPathTest, FillType) { + SkPath path; + path.setFillType(SkPath::kWinding_FillType); + NSBezierPath* result = CreateNSBezierPathFromSkPath(path); + EXPECT_EQ(NSNonZeroWindingRule, [result windingRule]); + + path.setFillType(SkPath::kEvenOdd_FillType); + result = CreateNSBezierPathFromSkPath(path); + EXPECT_EQ(NSEvenOddWindingRule, [result windingRule]); +} + +// Check that a path containing multiple subpaths, in this case two rectangles, +// is correctly converted to a NSBezierPath. +TEST(CreateNSBezierPathFromSkPathTest, TwoRectanglesPath) { + const SkRect rects[] = { + {0, 0, 50, 50}, {100, 100, 150, 150}, + }; + const NSPoint inside_points[] = { + {1, 1}, {1, 49}, {49, 49}, {49, 1}, {25, 25}, + {101, 101}, {101, 149}, {149, 149}, {149, 101}, {125, 125}}; + const NSPoint outside_points[] = {{-1, -1}, {-1, 51}, {51, 51}, {51, -1}, + {99, 99}, {99, 151}, {151, 151}, {151, 99}, + {75, 75}, {-5, -5}}; + ASSERT_EQ(arraysize(inside_points), arraysize(outside_points)); + const Rect expected_bounds(0, 0, 150, 150); + + SkPath path; + path.addRect(rects[0]); + path.addRect(rects[1]); + NSBezierPath* result = CreateNSBezierPathFromSkPath(path); + + // Check points near the boundary of the path and verify that they are + // reported correctly as being inside/outside the path. + for (size_t i = 0; i < arraysize(inside_points); i++) { + EXPECT_TRUE([result containsPoint:inside_points[i]]); + EXPECT_FALSE([result containsPoint:outside_points[i]]); + } + + // Check that the returned result has the correct bounding box. GetBoundingBox + // rounds the coordinates to nearest integer values. + EXPECT_EQ(expected_bounds, GetBoundingBox(result)); +} + +// Test that an SKPath containing a circle is converted correctly to a +// NSBezierPath. +TEST(CreateNSBezierPathFromSkPathTest, CirclePath) { + const int kRadius = 5; + const int kCentreX = 10; + const int kCentreY = 15; + const double kCushion = 0.1; + // Expected bounding box of the circle. + const Rect expected_bounds(kCentreX - kRadius, kCentreY - kRadius, + 2 * kRadius, 2 * kRadius); + + SkPath path; + path.addCircle(SkIntToScalar(kCentreX), SkIntToScalar(kCentreY), + SkIntToScalar(kRadius)); + NSBezierPath* result = CreateNSBezierPathFromSkPath(path); + + // Check points near the boundary of the circle and verify that they are + // reported correctly as being inside/outside the path. + for (size_t deg = 0; deg < 360; deg++) { + NSPoint inside_point = + GetRadialPoint(kRadius - kCushion, deg, kCentreX, kCentreY); + NSPoint outside_point = + GetRadialPoint(kRadius + kCushion, deg, kCentreX, kCentreY); + EXPECT_TRUE([result containsPoint:inside_point]); + EXPECT_FALSE([result containsPoint:outside_point]); + } + + // Check that the returned result has the correct bounding box. GetBoundingBox + // rounds the coordinates to nearest integer values. + EXPECT_EQ(expected_bounds, GetBoundingBox(result)); + + // Check area of converted path is correct up to a certain tolerance value. To + // find the area of the NSBezierPath returned, flatten it i.e. convert it to a + // polygon. + [NSBezierPath setDefaultFlatness:0.01]; + NSBezierPath* polygon = [result bezierPathByFlatteningPath]; + const double kTolerance = 0.14; + EXPECT_NEAR(CalculateCircleArea(kRadius), CalculatePolygonArea(polygon), + kTolerance); +} + +// Test that an SKPath containing a rounded rectangle is converted correctly to +// a NSBezierPath. +TEST(CreateNSBezierPathFromSkPathTest, RoundedRectanglePath) { + const int kRectangleWidth = 50; + const int kRectangleHeight = 100; + const int kCornerRadius = 5; + const double kCushion = 0.1; + // Expected bounding box of the rounded rectangle. + const Rect expected_bounds(kRectangleWidth, kRectangleHeight); + + SkRRect rrect; + rrect.setRectXY(SkRect::MakeWH(kRectangleWidth, kRectangleHeight), + kCornerRadius, kCornerRadius); + + const NSPoint inside_points[] = { + // Bottom left corner. + {kCornerRadius / 2.0, kCornerRadius / 2.0}, + // Bottom right corner. + {kRectangleWidth - kCornerRadius / 2.0, kCornerRadius / 2.0}, + // Top Right corner. + {kRectangleWidth - kCornerRadius / 2.0, + kRectangleHeight - kCornerRadius / 2.0}, + // Top left corner. + {kCornerRadius / 2.0, kRectangleHeight - kCornerRadius / 2.0}, + // Bottom middle. + {kRectangleWidth / 2.0, kCushion}, + // Right middle. + {kRectangleWidth - kCushion, kRectangleHeight / 2.0}, + // Top middle. + {kRectangleWidth / 2.0, kRectangleHeight - kCushion}, + // Left middle. + {kCushion, kRectangleHeight / 2.0}}; + const NSPoint outside_points[] = { + // Bottom left corner. + {0, 0}, + // Bottom right corner. + {kRectangleWidth, 0}, + // Top right corner. + {kRectangleWidth, kRectangleHeight}, + // Top left corner. + {0, kRectangleHeight}, + // Bottom middle. + {kRectangleWidth / 2.0, -kCushion}, + // Right middle. + {kRectangleWidth + kCushion, kRectangleHeight / 2.0}, + // Top middle. + {kRectangleWidth / 2.0, kRectangleHeight + kCushion}, + // Left middle. + {-kCushion, kRectangleHeight / 2.0}}; + ASSERT_EQ(arraysize(inside_points), arraysize(outside_points)); + + SkPath path; + path.addRRect(rrect); + NSBezierPath* result = CreateNSBezierPathFromSkPath(path); + + // Check points near the boundary of the path and verify that they are + // reported correctly as being inside/outside the path. + for (size_t i = 0; i < arraysize(inside_points); i++) { + EXPECT_TRUE([result containsPoint:inside_points[i]]); + EXPECT_FALSE([result containsPoint:outside_points[i]]); + } + + // Check that the returned result has the correct bounding box. GetBoundingBox + // rounds the coordinates to nearest integer values. + EXPECT_EQ(expected_bounds, GetBoundingBox(result)); + + // Check area of converted path is correct up to a certain tolerance value. To + // find the area of the NSBezierPath returned, flatten it i.e. convert it to a + // polygon. + [NSBezierPath setDefaultFlatness:0.01]; + NSBezierPath* polygon = [result bezierPathByFlatteningPath]; + const double kTolerance = 0.14; + EXPECT_NEAR(CalculateRoundedRectangleArea(kRectangleWidth, kRectangleHeight, + kCornerRadius), + CalculatePolygonArea(polygon), kTolerance); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/platform_font_linux.cc b/chromium/ui/gfx/platform_font_linux.cc index 3c58ccb695b..dba65947477 100644 --- a/chromium/ui/gfx/platform_font_linux.cc +++ b/chromium/ui/gfx/platform_font_linux.cc @@ -196,7 +196,6 @@ int PlatformFontLinux::GetFontSize() const { } const FontRenderParams& PlatformFontLinux::GetFontRenderParams() { -#if defined(OS_CHROMEOS) float current_scale_factor = GetFontRenderParamsDeviceScaleFactor(); if (current_scale_factor != device_scale_factor_) { FontRenderParamsQuery query; @@ -207,7 +206,6 @@ const FontRenderParams& PlatformFontLinux::GetFontRenderParams() { font_render_params_ = gfx::GetFontRenderParams(query, nullptr); device_scale_factor_ = current_scale_factor; } -#endif return font_render_params_; } @@ -237,9 +235,7 @@ void PlatformFontLinux::InitFromDetails( font_size_pixels_ = font_size_pixels; style_ = style; -#if defined(OS_CHROMEOS) device_scale_factor_ = GetFontRenderParamsDeviceScaleFactor(); -#endif font_render_params_ = render_params; } @@ -249,9 +245,7 @@ void PlatformFontLinux::InitFromPlatformFont(const PlatformFontLinux* other) { font_family_ = other->font_family_; font_size_pixels_ = other->font_size_pixels_; style_ = other->style_; -#if defined(OS_CHROMEOS) device_scale_factor_ = other->device_scale_factor_; -#endif font_render_params_ = other->font_render_params_; if (!other->metrics_need_computation_) { diff --git a/chromium/ui/gfx/platform_font_linux.h b/chromium/ui/gfx/platform_font_linux.h index 52c4bceb76e..e990f33b1ed 100644 --- a/chromium/ui/gfx/platform_font_linux.h +++ b/chromium/ui/gfx/platform_font_linux.h @@ -84,9 +84,7 @@ class GFX_EXPORT PlatformFontLinux : public PlatformFont { std::string font_family_; int font_size_pixels_; int style_; -#if defined(OS_CHROMEOS) float device_scale_factor_; -#endif // Information describing how the font should be rendered. FontRenderParams font_render_params_; diff --git a/chromium/ui/gfx/range/range.cc b/chromium/ui/gfx/range/range.cc index 1c3968aa6e2..a6475cba2ec 100644 --- a/chromium/ui/gfx/range/range.cc +++ b/chromium/ui/gfx/range/range.cc @@ -7,7 +7,6 @@ #include <algorithm> #include <limits> -#include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -18,30 +17,30 @@ Range::Range() end_(0) { } -Range::Range(size_t start, size_t end) +Range::Range(uint32_t start, uint32_t end) : start_(start), end_(end) { } -Range::Range(size_t position) +Range::Range(uint32_t position) : start_(position), end_(position) { } // static const Range Range::InvalidRange() { - return Range(std::numeric_limits<size_t>::max()); + return Range(std::numeric_limits<uint32_t>::max()); } bool Range::IsValid() const { return *this != InvalidRange(); } -size_t Range::GetMin() const { +uint32_t Range::GetMin() const { return std::min(start(), end()); } -size_t Range::GetMax() const { +uint32_t Range::GetMax() const { return std::max(start(), end()); } @@ -68,8 +67,8 @@ bool Range::Contains(const Range& range) const { } Range Range::Intersect(const Range& range) const { - size_t min = std::max(GetMin(), range.GetMin()); - size_t max = std::min(GetMax(), range.GetMax()); + uint32_t min = std::max(GetMin(), range.GetMin()); + uint32_t max = std::min(GetMax(), range.GetMax()); if (min >= max) // No intersection. return InvalidRange(); @@ -78,7 +77,7 @@ Range Range::Intersect(const Range& range) const { } std::string Range::ToString() const { - return base::StringPrintf("{%" PRIuS ",%" PRIuS "}", start(), end()); + return base::StringPrintf("{%d,%d}", start(), end()); } std::ostream& operator<<(std::ostream& os, const Range& range) { diff --git a/chromium/ui/gfx/range/range.h b/chromium/ui/gfx/range/range.h index 741b318138d..3d9c724de55 100644 --- a/chromium/ui/gfx/range/range.h +++ b/chromium/ui/gfx/range/range.h @@ -6,6 +6,7 @@ #define UI_GFX_RANGE_RANGE_H_ #include <stddef.h> +#include <stdint.h> #include <ostream> #include <string> @@ -39,10 +40,10 @@ class GFX_EXPORT Range { Range(); // Initializes the range with a start and end. - Range(size_t start, size_t end); + Range(uint32_t start, uint32_t end); // Initializes the range with the same start and end positions. - explicit Range(size_t position); + explicit Range(uint32_t position); // Platform constructors. #if defined(OS_MACOSX) @@ -53,31 +54,30 @@ class GFX_EXPORT Range { Range(const CHARRANGE& range, LONG total_length = -1); #endif - // Returns a range that is invalid, which is {size_t_max,size_t_max}. + // Returns a range that is invalid, which is {UINT32_MAX,UINT32_MAX}. static const Range InvalidRange(); // Checks if the range is valid through comparison to InvalidRange(). bool IsValid() const; // Getters and setters. - size_t start() const { return start_; } - void set_start(size_t start) { start_ = start; } + uint32_t start() const { return start_; } + void set_start(uint32_t start) { start_ = start; } - size_t end() const { return end_; } - void set_end(size_t end) { end_ = end; } + uint32_t end() const { return end_; } + void set_end(uint32_t end) { end_ = end; } // Returns the absolute value of the length. - size_t length() const { - ptrdiff_t length = end() - start(); - return length >= 0 ? length : -length; + uint32_t length() const { + return GetMax() - GetMin(); } bool is_reversed() const { return start() > end(); } bool is_empty() const { return start() == end(); } // Returns the minimum and maximum values. - size_t GetMin() const; - size_t GetMax() const; + uint32_t GetMin() const; + uint32_t GetMax() const; bool operator==(const Range& other) const; bool operator!=(const Range& other) const; @@ -108,8 +108,11 @@ class GFX_EXPORT Range { std::string ToString() const; private: - size_t start_; - size_t end_; + // Note: we use uint32_t instead of size_t because this struct is sent over + // IPC which could span 32 & 64 bit processes. This is fine since text spans + // shouldn't exceed UINT32_MAX even on 64 bit builds. + uint32_t start_; + uint32_t end_; }; GFX_EXPORT std::ostream& operator<<(std::ostream& os, const Range& range); diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc index 1757575b32f..b133b976d96 100644 --- a/chromium/ui/gfx/render_text.cc +++ b/chromium/ui/gfx/render_text.cc @@ -139,11 +139,15 @@ void AddFadeEffect(const Rect& text_rect, // Creates a SkShader to fade the text, with |left_part| specifying the left // fade effect, if any, and |right_part| specifying the right fade effect. -skia::RefPtr<SkShader> CreateFadeShader(const FontList& font_list, - const Rect& text_rect, - const Rect& left_part, - const Rect& right_part, - SkColor color) { +sk_sp<SkShader> CreateFadeShader(const FontList& font_list, + const Rect& text_rect, + const Rect& left_part, + const Rect& right_part, + SkColor color) { + // The shader should only specify transparency of the fade itself, not the + // original transparency, which will be applied by the actual renderer. + DCHECK_EQ(SkColorGetA(color), static_cast<uint8_t>(0xff)); + // In general, fade down to 0 alpha. But when the available width is less // than four characters, linearly ramp up the fade target alpha to as high as // 20% at zero width. This allows the user to see the last faded characters a @@ -174,9 +178,9 @@ skia::RefPtr<SkShader> CreateFadeShader(const FontList& font_list, const SkPoint points[2] = { PointToSkPoint(text_rect.origin()), PointToSkPoint(text_rect.top_right()) }; - return skia::AdoptRef( - SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0], - colors.size(), SkShader::kClamp_TileMode)); + return + SkGradientShader::MakeLinear(&points[0], &colors[0], &positions[0], + colors.size(), SkShader::kClamp_TileMode); } // Converts a FontRenderParams::Hinting value to the corresponding @@ -236,8 +240,8 @@ SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas) SkiaTextRenderer::~SkiaTextRenderer() { } -void SkiaTextRenderer::SetDrawLooper(SkDrawLooper* draw_looper) { - paint_.setLooper(draw_looper); +void SkiaTextRenderer::SetDrawLooper(sk_sp<SkDrawLooper> draw_looper) { + paint_.setLooper(std::move(draw_looper)); } void SkiaTextRenderer::SetFontRenderParams(const FontRenderParams& params, @@ -269,8 +273,8 @@ void SkiaTextRenderer::SetForegroundColor(SkColor foreground) { paint_.setColor(foreground); } -void SkiaTextRenderer::SetShader(SkShader* shader) { - paint_.setShader(shader); +void SkiaTextRenderer::SetShader(sk_sp<SkShader> shader) { + paint_.setShader(std::move(shader)); } void SkiaTextRenderer::SetUnderlineMetrics(SkScalar thickness, @@ -415,6 +419,8 @@ LineSegment::~LineSegment() {} Line::Line() : preceding_heights(0), baseline(0) {} +Line::Line(const Line& other) = default; + Line::~Line() {} #if !defined(OS_MACOSX) @@ -632,7 +638,8 @@ void RenderText::MoveCursor(BreakType break_type, bool RenderText::MoveCursorTo(const SelectionModel& model) { // Enforce valid selection model components. size_t text_length = text().length(); - Range range(std::min(model.selection().start(), text_length), + Range range(std::min(model.selection().start(), + static_cast<uint32_t>(text_length)), std::min(model.caret_pos(), text_length)); // The current model only supports caret positions at valid cursor indices. if (!IsValidCursorIndex(range.start()) || !IsValidCursorIndex(range.end())) @@ -644,8 +651,9 @@ bool RenderText::MoveCursorTo(const SelectionModel& model) { } bool RenderText::SelectRange(const Range& range) { - Range sel(std::min(range.start(), text().length()), - std::min(range.end(), text().length())); + uint32_t text_length = static_cast<uint32_t>(text().length()); + Range sel(std::min(range.start(), text_length), + std::min(range.end(), text_length)); // Allow selection bounds at valid indicies amid multi-character graphemes. if (!IsValidLogicalIndex(sel.start()) || !IsValidLogicalIndex(sel.end())) return false; @@ -1227,16 +1235,13 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { text_rect.Inset(GetAlignmentOffset(0).x(), 0, 0, 0); // TODO(msw): Use the actual text colors corresponding to each faded part. - skia::RefPtr<SkShader> shader = + renderer->SetShader( CreateFadeShader(font_list(), text_rect, left_part, right_part, - colors_.breaks().front().second); - if (shader) - renderer->SetShader(shader.get()); + SkColorSetA(colors_.breaks().front().second, 0xff))); } void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { - skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(shadows_); - renderer->SetDrawLooper(looper.get()); + renderer->SetDrawLooper(CreateShadowDrawLooper(shadows_)); } base::i18n::TextDirection RenderText::GetTextDirection( diff --git a/chromium/ui/gfx/render_text.h b/chromium/ui/gfx/render_text.h index 522cc4d0d9b..5913f4dcb68 100644 --- a/chromium/ui/gfx/render_text.h +++ b/chromium/ui/gfx/render_text.h @@ -57,14 +57,14 @@ class GFX_EXPORT SkiaTextRenderer { explicit SkiaTextRenderer(Canvas* canvas); virtual ~SkiaTextRenderer(); - void SetDrawLooper(SkDrawLooper* draw_looper); + void SetDrawLooper(sk_sp<SkDrawLooper> draw_looper); void SetFontRenderParams(const FontRenderParams& params, bool subpixel_rendering_suppressed); void SetTypeface(SkTypeface* typeface); void SetTextSize(SkScalar size); void SetFontWithStyle(const Font& font, int font_style); void SetForegroundColor(SkColor foreground); - void SetShader(SkShader* shader); + void SetShader(sk_sp<SkShader> shader); // Sets underline metrics to use if the text will be drawn with an underline. // If not set, default values based on the size of the text will be used. The // two metrics must be set together. @@ -170,6 +170,7 @@ struct LineSegment { // A line of display text, comprised of a line segment list and some metrics. struct Line { Line(); + Line(const Line& other); ~Line(); // Segments that make up this line in visual order. diff --git a/chromium/ui/gfx/render_text_harfbuzz.cc b/chromium/ui/gfx/render_text_harfbuzz.cc index 39fc88cb2c1..590537ebe63 100644 --- a/chromium/ui/gfx/render_text_harfbuzz.cc +++ b/chromium/ui/gfx/render_text_harfbuzz.cc @@ -28,6 +28,7 @@ #include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/harfbuzz_font_skia.h" #include "ui/gfx/range/range_f.h" +#include "ui/gfx/skia_util.h" #include "ui/gfx/text_utils.h" #include "ui/gfx/utf16_indexing.h" @@ -460,7 +461,8 @@ class HarfBuzzLineBreaker { } const size_t valid_end_pos = std::max( - segment.char_range.start(), FindValidBoundaryBefore(text_, end_pos)); + segment.char_range.start(), + static_cast<uint32_t>(FindValidBoundaryBefore(text_, end_pos))); if (end_pos != valid_end_pos) { end_pos = valid_end_pos; width = run.GetGlyphWidthForCharRange( @@ -472,8 +474,9 @@ class HarfBuzzLineBreaker { // not separate surrogate pair or combining characters. // See RenderTextTest.Multiline_MinWidth for an example. if (width == 0 && available_width_ == max_width_) { - end_pos = std::min(segment.char_range.end(), - FindValidBoundaryAfter(text_, end_pos + 1)); + end_pos = std::min( + segment.char_range.end(), + static_cast<uint32_t>(FindValidBoundaryAfter(text_, end_pos + 1))); } return end_pos; @@ -1501,12 +1504,14 @@ bool RenderTextHarfBuzz::ShapeRunWithFont(const base::string16& text, DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16_t>::max()); run->glyphs[i] = static_cast<uint16_t>(infos[i].codepoint); run->glyph_to_char[i] = infos[i].cluster; - const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); - const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); + const SkScalar x_offset = + HarfBuzzUnitsToSkiaScalar(hb_positions[i].x_offset); + const SkScalar y_offset = + HarfBuzzUnitsToSkiaScalar(hb_positions[i].y_offset); run->positions[i].set(run->width + x_offset, -y_offset); run->width += (glyph_width_for_test_ > 0) ? glyph_width_for_test_ - : SkFixedToFloat(hb_positions[i].x_advance); + : HarfBuzzUnitsToFloat(hb_positions[i].x_advance); // Round run widths if subpixel positioning is off to match native behavior. if (!run->render_params.subpixel_positioning) run->width = std::floor(run->width + 0.5f); diff --git a/chromium/ui/gfx/render_text_harfbuzz.h b/chromium/ui/gfx/render_text_harfbuzz.h index e91effe694b..31c5294ad00 100644 --- a/chromium/ui/gfx/render_text_harfbuzz.h +++ b/chromium/ui/gfx/render_text_harfbuzz.h @@ -173,6 +173,8 @@ class GFX_EXPORT RenderTextHarfBuzz : public RenderText { void DrawVisualText(internal::SkiaTextRenderer* renderer) override; private: + friend class test::RenderTextTestApi; + FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_HorizontalAlignment); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_NormalWidth); FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_WordWrapBehavior); diff --git a/chromium/ui/gfx/render_text_mac.h b/chromium/ui/gfx/render_text_mac.h index 042029aa3de..58c027a6552 100644 --- a/chromium/ui/gfx/render_text_mac.h +++ b/chromium/ui/gfx/render_text_mac.h @@ -75,6 +75,7 @@ class GFX_EXPORT RenderTextMac : public RenderText { bool diagonal_strike; TextRun(); + TextRun(const TextRun& other); ~TextRun(); }; diff --git a/chromium/ui/gfx/render_text_mac.mm b/chromium/ui/gfx/render_text_mac.mm index 3a6d01b9f3a..19257a84eb6 100644 --- a/chromium/ui/gfx/render_text_mac.mm +++ b/chromium/ui/gfx/render_text_mac.mm @@ -203,6 +203,9 @@ void RenderTextMac::DrawVisualText(internal::SkiaTextRenderer* renderer) { ApplyFadeEffects(renderer); ApplyTextShadows(renderer); + renderer->SetFontRenderParams( + font_list().GetPrimaryFont().GetFontRenderParams(), + subpixel_rendering_suppressed()); for (size_t i = 0; i < runs_.size(); ++i) { const TextRun& run = runs_[i]; @@ -237,6 +240,8 @@ RenderTextMac::TextRun::TextRun() strike(false), diagonal_strike(false) {} +RenderTextMac::TextRun::TextRun(const TextRun& other) = default; + RenderTextMac::TextRun::~TextRun() {} float RenderTextMac::GetLayoutTextWidth() { diff --git a/chromium/ui/gfx/render_text_unittest.cc b/chromium/ui/gfx/render_text_unittest.cc index 51e1ac7a861..9186d4d05f0 100644 --- a/chromium/ui/gfx/render_text_unittest.cc +++ b/chromium/ui/gfx/render_text_unittest.cc @@ -3017,9 +3017,9 @@ TEST_F(RenderTextTest, TextDoesntClip) { const Size kCanvasSize(300, 50); const int kTestSize = 10; - skia::RefPtr<SkSurface> surface = skia::AdoptRef( - SkSurface::NewRasterN32Premul(kCanvasSize.width(), kCanvasSize.height())); - Canvas canvas(surface->getCanvas(), 1.0f); + sk_sp<SkSurface> surface = + SkSurface::MakeRasterN32Premul(kCanvasSize.width(), kCanvasSize.height()); + Canvas canvas(skia::SharePtr(surface->getCanvas()), 1.0f); scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetHorizontalAlignment(ALIGN_LEFT); render_text->SetColor(SK_ColorBLACK); @@ -3048,27 +3048,36 @@ TEST_F(RenderTextTest, TextDoesntClip) { kCanvasSize.height()); { #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) + 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(), - kTestSize); -#endif + top_test_height); +#endif // !OS_CHROMEOS } { + 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) { + 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, - kTestSize + string_size.height(), - kCanvasSize.width(), kTestSize); + rect_buffer.EnsureSolidRect(SK_ColorWHITE, 0, bottom_test_y, + kCanvasSize.width(), bottom_test_height); } { SCOPED_TRACE("TextDoesntClip Left Side"); -#if defined(OS_WIN) - // TODO(dschuyler): On Windows XP the Unicode test draws to the left edge - // as if it is ignoring the SetDisplayRect shift by kTestSize. This - // appears to be a preexisting issue that wasn't revealed by the prior - // unit tests. -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) - // TODO(dschuyler): On Windows (non-XP), Chrome OS and Mac smoothing draws +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) + // TODO(dschuyler): On Windows, Chrome OS and Mac smoothing draws to the // left of text. This appears to be a preexisting issue that wasn't // revealed by the prior unit tests. rect_buffer.EnsureSolidRect(SK_ColorWHITE, 0, kTestSize, kTestSize - 1, @@ -3099,9 +3108,9 @@ TEST_F(RenderTextTest, TextDoesClip) { const Size kCanvasSize(300, 50); const int kTestSize = 10; - skia::RefPtr<SkSurface> surface = skia::AdoptRef( - SkSurface::NewRasterN32Premul(kCanvasSize.width(), kCanvasSize.height())); - Canvas canvas(surface->getCanvas(), 1.0f); + sk_sp<SkSurface> surface = + SkSurface::MakeRasterN32Premul(kCanvasSize.width(), kCanvasSize.height()); + Canvas canvas(skia::SharePtr(surface->getCanvas()), 1.0f); scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); render_text->SetHorizontalAlignment(ALIGN_LEFT); render_text->SetColor(SK_ColorBLACK); diff --git a/chromium/ui/gfx/screen.cc b/chromium/ui/gfx/screen.cc index 06ec78ec885..a07e9218b1f 100644 --- a/chromium/ui/gfx/screen.cc +++ b/chromium/ui/gfx/screen.cc @@ -2,16 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/logging.h" #include "ui/gfx/screen.h" -#include "ui/gfx/screen_type_delegate.h" + +#include "ui/gfx/geometry/rect.h" namespace gfx { namespace { -Screen* g_screen_[SCREEN_TYPE_LAST + 1]; -ScreenTypeDelegate* g_screen_type_delegate_ = NULL; +Screen* g_screen; } // namespace @@ -22,37 +21,30 @@ Screen::~Screen() { } // static -Screen* Screen::GetScreenFor(NativeView view) { - ScreenType type = SCREEN_TYPE_NATIVE; - if (g_screen_type_delegate_) - type = g_screen_type_delegate_->GetScreenTypeForNativeView(view); - if (type == SCREEN_TYPE_NATIVE) - return GetNativeScreen(); - DCHECK(g_screen_[type]); - return g_screen_[type]; -} - -// static -void Screen::SetScreenInstance(ScreenType type, Screen* instance) { - DCHECK_LE(type, SCREEN_TYPE_LAST); - g_screen_[type] = instance; +Screen* Screen::GetScreen() { +#if defined(OS_MACOSX) || defined(OS_ANDROID) + // TODO(scottmg): https://crbug.com/558054 + if (!g_screen) + g_screen = CreateNativeScreen(); +#endif + return g_screen; } // static -Screen* Screen::GetScreenByType(ScreenType type) { - return g_screen_[type]; +void Screen::SetScreenInstance(Screen* instance) { + g_screen = instance; } -// static -void Screen::SetScreenTypeDelegate(ScreenTypeDelegate* delegate) { - g_screen_type_delegate_ = delegate; +gfx::Rect Screen::ScreenToDIPRectInWindow(NativeView view, + const gfx::Rect& screen_rect) const { + float scale = GetDisplayNearestWindow(view).device_scale_factor(); + return ScaleToEnclosingRect(screen_rect, 1.0f / scale); } -// static -Screen* Screen::GetNativeScreen() { - if (!g_screen_[SCREEN_TYPE_NATIVE]) - g_screen_[SCREEN_TYPE_NATIVE] = CreateNativeScreen(); - return g_screen_[SCREEN_TYPE_NATIVE]; +gfx::Rect Screen::DIPToScreenRectInWindow(NativeView view, + const gfx::Rect& dip_rect) const { + float scale = GetDisplayNearestWindow(view).device_scale_factor(); + return ScaleToEnclosingRect(dip_rect, scale); } } // namespace gfx diff --git a/chromium/ui/gfx/screen.h b/chromium/ui/gfx/screen.h index 107ee44e243..9352e792cb4 100644 --- a/chromium/ui/gfx/screen.h +++ b/chromium/ui/gfx/screen.h @@ -9,51 +9,33 @@ #include "base/macros.h" #include "ui/gfx/display.h" -#include "ui/gfx/geometry/point.h" #include "ui/gfx/gfx_export.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gfx/screen_type_delegate.h" namespace gfx { + class DisplayObserver; +class Point; class Rect; // A utility class for getting various info about screen size, displays, // cursor position, etc. // // Note that this class does not represent an individual display connected to a -// computer -- see the Display class for that. A single Screen object exists on -// most operating systems regardless of the number of connected displays. On -// Windows 8, two Screens exist: one for Metro UI and another for the desktop. +// computer -- see the Display class for that. A single Screen object exists +// regardless of the number of connected displays. class GFX_EXPORT Screen { public: - // Retrieves the Screen that the specified NativeView belongs to. A value of - // NULL is treated as |SCREEN_TYPE_NATIVE|. - static Screen* GetScreenFor(NativeView view); - - // Returns the SCREEN_TYPE_NATIVE Screen. This should be used with caution, - // as it is likely to be incorrect for code that runs on Windows. - static Screen* GetNativeScreen(); - - // Sets the global screen for a particular screen type. Only the _NATIVE - // ScreenType must be provided. - // NOTE: this does not take ownership of |screen|. Tests must be sure to - // reset any state they install. - static void SetScreenInstance(ScreenType type, Screen* instance); - - // Returns the global screen for a particular type. Types other than _NATIVE - // may be NULL. - static Screen* GetScreenByType(ScreenType type); - - // Sets the global ScreenTypeDelegate. May be left unset if the platform - // uses only the _NATIVE ScreenType. - // NOTE: this does not take ownership of |delegate|. Tests must be sure to - // reset any state they install. - static void SetScreenTypeDelegate(ScreenTypeDelegate* delegate); - Screen(); virtual ~Screen(); + // Retrieves the single Screen object. + static Screen* GetScreen(); + + // Sets the global screen. NOTE: this does not take ownership of |screen|. + // Tests must be sure to reset any state they install. + static void SetScreenInstance(Screen* instance); + // Returns the current absolute position of the mouse pointer. virtual gfx::Point GetCursorScreenPoint() = 0; @@ -91,6 +73,18 @@ class GFX_EXPORT Screen { virtual void AddObserver(DisplayObserver* observer) = 0; virtual void RemoveObserver(DisplayObserver* observer) = 0; + // Converts |screen_rect| to DIP coordinates in the context of |view| clamping + // to the enclosing rect if the coordinates do not fall on pixel boundaries. + // If |view| is null, the primary display is used as the context. + virtual gfx::Rect ScreenToDIPRectInWindow(NativeView view, + const gfx::Rect& screen_rect) const; + + // Converts |dip_rect| to screen coordinates in the context of |view| clamping + // to the enclosing rect if the coordinates do not fall on pixel boundaries. + // If |view| is null, the primary display is used as the context. + virtual gfx::Rect DIPToScreenRectInWindow(NativeView view, + const gfx::Rect& dip_rect) const; + private: DISALLOW_COPY_AND_ASSIGN(Screen); }; diff --git a/chromium/ui/gfx/screen_mac.mm b/chromium/ui/gfx/screen_mac.mm index 6fb7c6e8dc2..26e0278fa5a 100644 --- a/chromium/ui/gfx/screen_mac.mm +++ b/chromium/ui/gfx/screen_mac.mm @@ -13,9 +13,11 @@ #include "base/logging.h" #include "base/mac/sdk_forward_declarations.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/timer/timer.h" #include "ui/gfx/display.h" #include "ui/gfx/display_change_notifier.h" +#include "ui/gfx/mac/coordinate_conversion.h" namespace { @@ -23,16 +25,6 @@ namespace { // See comments in ScreenMac::HandleDisplayReconfiguration. const int64_t kConfigureDelayMs = 500; -gfx::Rect ConvertCoordinateSystem(NSRect ns_rect) { - // Primary monitor is defined as the monitor with the menubar, - // which is always at index 0. - NSScreen* primary_screen = [[NSScreen screens] firstObject]; - float primary_screen_height = [primary_screen frame].size.height; - gfx::Rect rect(NSRectToCGRect(ns_rect)); - rect.set_y(primary_screen_height - rect.y() - rect.height()); - return rect; -} - NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) { // Default to the monitor with the current keyboard focus, in case // |match_rect| is not on any screen at all. @@ -40,7 +32,7 @@ NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) { int max_area = 0; for (NSScreen* screen in [NSScreen screens]) { - gfx::Rect monitor_area = ConvertCoordinateSystem([screen frame]); + gfx::Rect monitor_area = gfx::ScreenRectFromNSRect([screen frame]); gfx::Rect intersection = gfx::IntersectRects(monitor_area, match_rect); int area = intersection.width() * intersection.height(); if (area > max_area) { @@ -69,14 +61,10 @@ gfx::Display GetDisplayForScreen(NSScreen* screen) { visible_frame.size.height); display.set_work_area(work_area); } else { - display.set_bounds(ConvertCoordinateSystem(frame)); - display.set_work_area(ConvertCoordinateSystem(visible_frame)); + display.set_bounds(gfx::ScreenRectFromNSRect(frame)); + display.set_work_area(gfx::ScreenRectFromNSRect(visible_frame)); } - CGFloat scale; - if ([screen respondsToSelector:@selector(backingScaleFactor)]) - scale = [screen backingScaleFactor]; - else - scale = [screen userSpaceScaleFactor]; + CGFloat scale = [screen backingScaleFactor]; if (gfx::Display::HasForceDeviceScaleFactor()) scale = gfx::Display::GetForcedDeviceScaleFactor(); @@ -103,11 +91,8 @@ class ScreenMac : public gfx::Screen { } gfx::Point GetCursorScreenPoint() override { - NSPoint mouseLocation = [NSEvent mouseLocation]; // Flip coordinates to gfx (0,0 in top-left corner) using primary screen. - NSScreen* screen = [[NSScreen screens] firstObject]; - mouseLocation.y = NSMaxY([screen frame]) - mouseLocation.y; - return gfx::Point(mouseLocation.x, mouseLocation.y); + return gfx::ScreenPointFromNSPoint([NSEvent mouseLocation]); } gfx::NativeWindow GetWindowUnderCursor() override { diff --git a/chromium/ui/gfx/screen_type_delegate.h b/chromium/ui/gfx/screen_type_delegate.h deleted file mode 100644 index 03dad8f8dea..00000000000 --- a/chromium/ui/gfx/screen_type_delegate.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 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_GFX_SCREEN_TYPE_DELEGATE_H_ -#define UI_GFX_SCREEN_TYPE_DELEGATE_H_ - -#include "build/build_config.h" -#include "ui/gfx/native_widget_types.h" - -namespace gfx { - -enum ScreenType { - SCREEN_TYPE_NATIVE = 0, -#if defined(OS_CHROMEOS) - SCREEN_TYPE_ALTERNATE = SCREEN_TYPE_NATIVE, -#else - SCREEN_TYPE_ALTERNATE, -#endif - SCREEN_TYPE_LAST = SCREEN_TYPE_ALTERNATE, -}; - -class GFX_EXPORT ScreenTypeDelegate { - public: - virtual ~ScreenTypeDelegate() {} - - // Determines which ScreenType a given |view| belongs to. - virtual ScreenType GetScreenTypeForNativeView(NativeView view) = 0; -}; - -} // namespace gfx - -#endif // UI_GFX_SCREEN_TYPE_DELEGATE_H_ diff --git a/chromium/ui/gfx/screen_unittest.cc b/chromium/ui/gfx/screen_unittest.cc index db5a03ffec9..8b63ac70b52 100644 --- a/chromium/ui/gfx/screen_unittest.cc +++ b/chromium/ui/gfx/screen_unittest.cc @@ -10,15 +10,14 @@ namespace { TEST(ScreenTest, GetPrimaryDisplaySize) { // We aren't actually testing that it's correct, just that it's sane. - const gfx::Size size = - gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().size(); + const gfx::Size size = gfx::Screen::GetScreen()->GetPrimaryDisplay().size(); EXPECT_GE(size.width(), 1); EXPECT_GE(size.height(), 1); } TEST(ScreenTest, GetNumDisplays) { // We aren't actually testing that it's correct, just that it's sane. - EXPECT_GE(gfx::Screen::GetNativeScreen()->GetNumDisplays(), 1); + EXPECT_GE(gfx::Screen::GetScreen()->GetNumDisplays(), 1); } } // namespace diff --git a/chromium/ui/gfx/screen_win.cc b/chromium/ui/gfx/screen_win.cc deleted file mode 100644 index 3199f7acaac..00000000000 --- a/chromium/ui/gfx/screen_win.cc +++ /dev/null @@ -1,216 +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/gfx/screen_win.h" - -#include <windows.h> -#include <stdint.h> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/hash.h" -#include "base/logging.h" -#include "base/strings/utf_string_conversions.h" -#include "base/win/win_util.h" -#include "ui/gfx/display.h" -#include "ui/gfx/win/dpi.h" - -namespace { - -MONITORINFOEX GetMonitorInfoForMonitor(HMONITOR monitor) { - MONITORINFOEX monitor_info; - ZeroMemory(&monitor_info, sizeof(MONITORINFOEX)); - monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(monitor, &monitor_info); - return monitor_info; -} - -gfx::Display GetDisplay(const MONITORINFOEX& monitor_info) { - int64_t id = - static_cast<int64_t>(base::Hash(base::WideToUTF8(monitor_info.szDevice))); - gfx::Rect bounds = gfx::Rect(monitor_info.rcMonitor); - gfx::Display display(id); - display.set_bounds(gfx::win::ScreenToDIPRect(bounds)); - display.set_work_area( - gfx::win::ScreenToDIPRect(gfx::Rect(monitor_info.rcWork))); - display.SetScaleAndBounds(gfx::GetDPIScale(), bounds); - - DEVMODE mode; - memset(&mode, 0, sizeof(DEVMODE)); - mode.dmSize = sizeof(DEVMODE); - mode.dmDriverExtra = 0; - if (EnumDisplaySettings(monitor_info.szDevice, - ENUM_CURRENT_SETTINGS, - &mode)) { - switch (mode.dmDisplayOrientation) { - case DMDO_DEFAULT: - display.set_rotation(gfx::Display::ROTATE_0); - break; - case DMDO_90: - display.set_rotation(gfx::Display::ROTATE_90); - break; - case DMDO_180: - display.set_rotation(gfx::Display::ROTATE_180); - break; - case DMDO_270: - display.set_rotation(gfx::Display::ROTATE_270); - break; - default: - NOTREACHED(); - } - } - - return display; -} - -BOOL CALLBACK EnumMonitorCallback(HMONITOR monitor, - HDC hdc, - LPRECT rect, - LPARAM data) { - std::vector<gfx::Display>* all_displays = - reinterpret_cast<std::vector<gfx::Display>*>(data); - DCHECK(all_displays); - - MONITORINFOEX monitor_info = GetMonitorInfoForMonitor(monitor); - gfx::Display display = GetDisplay(monitor_info); - all_displays->push_back(display); - return TRUE; -} - -std::vector<gfx::Display> GetDisplays() { - std::vector<gfx::Display> displays; - EnumDisplayMonitors(NULL, NULL, EnumMonitorCallback, - reinterpret_cast<LPARAM>(&displays)); - return displays; -} - -} // namespace - -namespace gfx { - -ScreenWin::ScreenWin() - : singleton_hwnd_observer_(new SingletonHwndObserver( - base::Bind(&ScreenWin::OnWndProc, base::Unretained(this)))), - displays_(GetDisplays()) { -} - -ScreenWin::~ScreenWin() {} - -HWND ScreenWin::GetHWNDFromNativeView(NativeView window) const { - NOTREACHED(); - return NULL; -} - -NativeWindow ScreenWin::GetNativeWindowFromHWND(HWND hwnd) const { - NOTREACHED(); - return NULL; -} - -gfx::Point ScreenWin::GetCursorScreenPoint() { - POINT pt; - GetCursorPos(&pt); - gfx::Point cursor_pos_pixels(pt); - return gfx::win::ScreenToDIPPoint(cursor_pos_pixels); -} - -gfx::NativeWindow ScreenWin::GetWindowUnderCursor() { - POINT cursor_loc; - HWND hwnd = GetCursorPos(&cursor_loc) ? WindowFromPoint(cursor_loc) : NULL; - return GetNativeWindowFromHWND(hwnd); -} - -gfx::NativeWindow ScreenWin::GetWindowAtScreenPoint(const gfx::Point& point) { - gfx::Point point_in_pixels = gfx::win::DIPToScreenPoint(point); - return GetNativeWindowFromHWND(WindowFromPoint(point_in_pixels.ToPOINT())); -} - -int ScreenWin::GetNumDisplays() const { - return GetSystemMetrics(SM_CMONITORS); -} - -std::vector<gfx::Display> ScreenWin::GetAllDisplays() const { - return displays_; -} - -gfx::Display ScreenWin::GetDisplayNearestWindow(gfx::NativeView window) const { - HWND window_hwnd = GetHWNDFromNativeView(window); - if (!window_hwnd) { - // When |window| isn't rooted to a display, we should just return the - // default display so we get some correct display information like the - // scaling factor. - return GetPrimaryDisplay(); - } - - MONITORINFOEX monitor_info; - monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(MonitorFromWindow(window_hwnd, MONITOR_DEFAULTTONEAREST), - &monitor_info); - return GetDisplay(monitor_info); -} - -gfx::Display ScreenWin::GetDisplayNearestPoint(const gfx::Point& point) const { - gfx::Point point_in_pixels = gfx::win::DIPToScreenPoint(point); - POINT initial_loc = { point_in_pixels.x(), point_in_pixels.y() }; - HMONITOR monitor = MonitorFromPoint(initial_loc, MONITOR_DEFAULTTONEAREST); - MONITORINFOEX mi; - ZeroMemory(&mi, sizeof(MONITORINFOEX)); - mi.cbSize = sizeof(mi); - if (monitor && GetMonitorInfo(monitor, &mi)) { - return GetDisplay(mi); - } - return gfx::Display(); -} - -gfx::Display ScreenWin::GetDisplayMatching(const gfx::Rect& match_rect) const { - RECT other_bounds_rect = match_rect.ToRECT(); - MONITORINFOEX monitor_info = GetMonitorInfoForMonitor(MonitorFromRect( - &other_bounds_rect, MONITOR_DEFAULTTONEAREST)); - return GetDisplay(monitor_info); -} - -gfx::Display ScreenWin::GetPrimaryDisplay() const { - MONITORINFOEX mi = GetMonitorInfoForMonitor( - MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY)); - gfx::Display display = GetDisplay(mi); - // TODO(kevers|girard): Test if these checks can be reintroduced for high-DIP - // once more of the app is DIP-aware. - if (GetDPIScale() == 1.0) { - DCHECK_EQ(GetSystemMetrics(SM_CXSCREEN), display.size().width()); - DCHECK_EQ(GetSystemMetrics(SM_CYSCREEN), display.size().height()); - } - return display; -} - -void ScreenWin::AddObserver(DisplayObserver* observer) { - change_notifier_.AddObserver(observer); -} - -void ScreenWin::RemoveObserver(DisplayObserver* observer) { - change_notifier_.RemoveObserver(observer); -} - -void ScreenWin::OnWndProc(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam) { - if (message != WM_DISPLAYCHANGE) - return; - - std::vector<gfx::Display> old_displays = displays_; - displays_ = GetDisplays(); - - change_notifier_.NotifyDisplaysChanged(old_displays, displays_); -} - -// static -std::vector<gfx::Display> ScreenWin::GetDisplaysForMonitorInfos( - const std::vector<MONITORINFOEX>& monitor_infos) { - std::vector<gfx::Display> displays; - for (const MONITORINFOEX& monitor_info : monitor_infos) - displays.push_back(GetDisplay(monitor_info)); - - return displays; -} - -} // namespace gfx diff --git a/chromium/ui/gfx/screen_win.h b/chromium/ui/gfx/screen_win.h deleted file mode 100644 index b1488ae05bb..00000000000 --- a/chromium/ui/gfx/screen_win.h +++ /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. - -#ifndef UI_GFX_SCREEN_WIN_H_ -#define UI_GFX_SCREEN_WIN_H_ - -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "ui/gfx/display_change_notifier.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/screen.h" -#include "ui/gfx/win/singleton_hwnd_observer.h" - -namespace gfx { - -class GFX_EXPORT ScreenWin : public Screen { - public: - ScreenWin(); - ~ScreenWin() override; - - // Returns the HWND associated with the NativeView. - virtual HWND GetHWNDFromNativeView(NativeView window) const; - - // Returns the NativeView associated with the HWND. - virtual NativeWindow GetNativeWindowFromHWND(HWND hwnd) const; - - protected: - // Overridden from gfx::Screen: - gfx::Point GetCursorScreenPoint() override; - gfx::NativeWindow GetWindowUnderCursor() override; - gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override; - int GetNumDisplays() const override; - std::vector<gfx::Display> GetAllDisplays() const override; - gfx::Display GetDisplayNearestWindow(gfx::NativeView window) const override; - gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override; - gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override; - gfx::Display GetPrimaryDisplay() const override; - void AddObserver(DisplayObserver* observer) override; - void RemoveObserver(DisplayObserver* observer) override; - - private: - FRIEND_TEST_ALL_PREFIXES(ScreenWinTest, SingleDisplay1x); - FRIEND_TEST_ALL_PREFIXES(ScreenWinTest, SingleDisplay2x); - - void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); - - static std::vector<gfx::Display> GetDisplaysForMonitorInfos( - const std::vector<MONITORINFOEX>& monitor_infos); - - // Helper implementing the DisplayObserver handling. - gfx::DisplayChangeNotifier change_notifier_; - - scoped_ptr<SingletonHwndObserver> singleton_hwnd_observer_; - - // Current list of displays. - std::vector<gfx::Display> displays_; - - DISALLOW_COPY_AND_ASSIGN(ScreenWin); -}; - -} // namespace gfx - -#endif // UI_GFX_SCREEN_WIN_H_ diff --git a/chromium/ui/gfx/screen_win_unittest.cc b/chromium/ui/gfx/screen_win_unittest.cc deleted file mode 100644 index b1118c17e23..00000000000 --- a/chromium/ui/gfx/screen_win_unittest.cc +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gfx/screen_win.h" - -#include <cwchar> -#include <string> -#include <vector> - -#include <windows.h> -#include <stddef.h> - -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/display.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/win/dpi.h" - -namespace gfx { - -namespace { - -MONITORINFOEX CreateMonitorInfo(gfx::Rect monitor, - gfx::Rect work, - std::wstring device_name) { - MONITORINFOEX monitor_info; - ::ZeroMemory(&monitor_info, sizeof(monitor_info)); - monitor_info.cbSize = sizeof(monitor_info); - monitor_info.rcMonitor = monitor.ToRECT(); - monitor_info.rcWork = work.ToRECT(); - size_t device_char_count = ARRAYSIZE(monitor_info.szDevice); - wcsncpy(monitor_info.szDevice, device_name.c_str(), device_char_count); - monitor_info.szDevice[device_char_count-1] = L'\0'; - return monitor_info; -} - -} // namespace - -class ScreenWinTest : public testing::Test { - private: - void SetUp() override { - testing::Test::SetUp(); - gfx::SetDefaultDeviceScaleFactor(1.0); - } - - void TearDown() override { - gfx::SetDefaultDeviceScaleFactor(1.0); - testing::Test::TearDown(); - } -}; - -TEST_F(ScreenWinTest, SingleDisplay1x) { - std::vector<MONITORINFOEX> monitor_infos; - monitor_infos.push_back(CreateMonitorInfo(gfx::Rect(0, 0, 1920, 1200), - gfx::Rect(0, 0, 1920, 1100), - L"primary")); - std::vector<gfx::Display> displays = - ScreenWin::GetDisplaysForMonitorInfos(monitor_infos); - - ASSERT_EQ(1u, displays.size()); - EXPECT_EQ(gfx::Rect(0, 0, 1920, 1200), displays[0].bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 1920, 1100), displays[0].work_area()); -} - -TEST_F(ScreenWinTest, SingleDisplay2x) { - gfx::SetDefaultDeviceScaleFactor(2.0); - - std::vector<MONITORINFOEX> monitor_infos; - monitor_infos.push_back(CreateMonitorInfo(gfx::Rect(0, 0, 1920, 1200), - gfx::Rect(0, 0, 1920, 1100), - L"primary")); - std::vector<gfx::Display> displays = - ScreenWin::GetDisplaysForMonitorInfos(monitor_infos); - - ASSERT_EQ(1u, displays.size()); - EXPECT_EQ(gfx::Rect(0, 0, 960, 600), displays[0].bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 960, 550), displays[0].work_area()); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/selection_model.h b/chromium/ui/gfx/selection_model.h index 87a7a2d0b4e..73bf113da5c 100644 --- a/chromium/ui/gfx/selection_model.h +++ b/chromium/ui/gfx/selection_model.h @@ -77,7 +77,7 @@ class GFX_EXPORT SelectionModel { // WARNING: Generally the selection start should not be changed without // considering the effect on the caret affinity. - void set_selection_start(size_t pos) { selection_.set_start(pos); } + void set_selection_start(uint32_t pos) { selection_.set_start(pos); } bool operator==(const SelectionModel& sel) const; bool operator!=(const SelectionModel& sel) const { return !(*this == sel); } diff --git a/chromium/ui/gfx/skbitmap_operations.cc b/chromium/ui/gfx/skbitmap_operations.cc index 3865916c903..3b3a7e823f9 100644 --- a/chromium/ui/gfx/skbitmap_operations.cc +++ b/chromium/ui/gfx/skbitmap_operations.cc @@ -704,10 +704,9 @@ SkBitmap SkBitmapOperations::CreateColorMask(const SkBitmap& bitmap, SkCanvas canvas(color_mask); - skia::RefPtr<SkColorFilter> color_filter = skia::AdoptRef( - SkColorFilter::CreateModeFilter(c, SkXfermode::kSrcIn_Mode)); SkPaint paint; - paint.setColorFilter(color_filter.get()); + paint.setColorFilter( + SkColorFilter::MakeModeFilter(c, SkXfermode::kSrcIn_Mode)); canvas.drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint); return color_mask; } diff --git a/chromium/ui/gfx/skia_util.cc b/chromium/ui/gfx/skia_util.cc index c1e6c184ce1..956a2f08755 100644 --- a/chromium/ui/gfx/skia_util.cc +++ b/chromium/ui/gfx/skia_util.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include <stdint.h> +#include "base/numerics/safe_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkColorPriv.h" @@ -90,14 +91,14 @@ void TransformToFlattenedSkMatrix(const gfx::Transform& transform, flattened->set(8, SkMScalarToScalar(transform.matrix().get(3, 3))); } -skia::RefPtr<SkShader> CreateImageRepShader(const gfx::ImageSkiaRep& image_rep, +sk_sp<SkShader> CreateImageRepShader(const gfx::ImageSkiaRep& image_rep, SkShader::TileMode tile_mode, const SkMatrix& local_matrix) { return CreateImageRepShaderForScale(image_rep, tile_mode, local_matrix, image_rep.scale()); } -skia::RefPtr<SkShader> CreateImageRepShaderForScale( +sk_sp<SkShader> CreateImageRepShaderForScale( const gfx::ImageSkiaRep& image_rep, SkShader::TileMode tile_mode, const SkMatrix& local_matrix, @@ -114,12 +115,11 @@ skia::RefPtr<SkShader> CreateImageRepShaderForScale( shader_scale.setScaleX(local_matrix.getScaleX() / scale); shader_scale.setScaleY(local_matrix.getScaleY() / scale); - skia::RefPtr<SkShader> shader = skia::AdoptRef(SkShader::CreateBitmapShader( - image_rep.sk_bitmap(), tile_mode, tile_mode, &shader_scale)); - return shader; + return SkShader::MakeBitmapShader( + image_rep.sk_bitmap(), tile_mode, tile_mode, &shader_scale); } -skia::RefPtr<SkShader> CreateGradientShader(int start_point, +sk_sp<SkShader> CreateGradientShader(int start_point, int end_point, SkColor start_color, SkColor end_color) { @@ -128,8 +128,8 @@ skia::RefPtr<SkShader> CreateGradientShader(int start_point, grad_points[0].iset(0, start_point); grad_points[1].iset(0, end_point); - return skia::AdoptRef(SkGradientShader::CreateLinear( - grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode)); + return SkGradientShader::MakeLinear( + grad_points, grad_colors, NULL, 2, SkShader::kClamp_TileMode); } static SkScalar RadiusToSigma(double radius) { @@ -138,10 +138,10 @@ static SkScalar RadiusToSigma(double radius) { return radius > 0 ? SkDoubleToScalar(0.57735f * radius + 0.5) : 0; } -skia::RefPtr<SkDrawLooper> CreateShadowDrawLooper( +sk_sp<SkDrawLooper> CreateShadowDrawLooper( const std::vector<ShadowValue>& shadows) { if (shadows.empty()) - return skia::RefPtr<SkDrawLooper>(); + return nullptr; SkLayerDrawLooper::Builder looper_builder; @@ -164,16 +164,15 @@ skia::RefPtr<SkDrawLooper> CreateShadowDrawLooper( SkBlurMaskFilter::Create(kNormal_SkBlurStyle, RadiusToSigma(shadow.blur() / 2), SkBlurMaskFilter::kHighQuality_BlurFlag)); - skia::RefPtr<SkColorFilter> color_filter = skia::AdoptRef( - SkColorFilter::CreateModeFilter(shadow.color(), - SkXfermode::kSrcIn_Mode)); SkPaint* paint = looper_builder.addLayer(layer_info); paint->setMaskFilter(blur_mask.get()); - paint->setColorFilter(color_filter.get()); + paint->setColorFilter( + SkColorFilter::MakeModeFilter(shadow.color(), + SkXfermode::kSrcIn_Mode)); } - return skia::AdoptRef<SkDrawLooper>(looper_builder.detachLooper()); + return looper_builder.detach(); } bool BitmapsAreEqual(const SkBitmap& bitmap1, const SkBitmap& bitmap2) { @@ -226,4 +225,21 @@ void QuadFToSkPoints(const gfx::QuadF& quad, SkPoint points[4]) { points[3] = PointFToSkPoint(quad.p4()); } +// We treat HarfBuzz ints as 16.16 fixed-point. +static const int kHbUnit1 = 1 << 16; + +int SkiaScalarToHarfBuzzUnits(SkScalar value) { + return base::saturated_cast<int>(value * kHbUnit1); +} + +SkScalar HarfBuzzUnitsToSkiaScalar(int value) { + static const SkScalar kSkToHbRatio = SK_Scalar1 / kHbUnit1; + return kSkToHbRatio * value; +} + +float HarfBuzzUnitsToFloat(int value) { + static const float kFloatToHbRatio = 1.0f / kHbUnit1; + return kFloatToHbRatio * value; +} + } // namespace gfx diff --git a/chromium/ui/gfx/skia_util.h b/chromium/ui/gfx/skia_util.h index 25c96d03705..9fd880b2cc4 100644 --- a/chromium/ui/gfx/skia_util.h +++ b/chromium/ui/gfx/skia_util.h @@ -54,13 +54,13 @@ GFX_EXPORT void TransformToFlattenedSkMatrix(const gfx::Transform& transform, // TODO(pkotwicz): Allow shader's local matrix to be changed after the shader // is created. // -GFX_EXPORT skia::RefPtr<SkShader> CreateImageRepShader( +GFX_EXPORT sk_sp<SkShader> CreateImageRepShader( const gfx::ImageSkiaRep& image_rep, SkShader::TileMode tile_mode, const SkMatrix& local_matrix); // Creates a bitmap shader for the image rep with the passed in scale factor. -GFX_EXPORT skia::RefPtr<SkShader> CreateImageRepShaderForScale( +GFX_EXPORT sk_sp<SkShader> CreateImageRepShaderForScale( const gfx::ImageSkiaRep& image_rep, SkShader::TileMode tile_mode, const SkMatrix& local_matrix, @@ -68,15 +68,15 @@ GFX_EXPORT skia::RefPtr<SkShader> CreateImageRepShaderForScale( // Creates a vertical gradient shader. The caller owns the shader. // Example usage to avoid leaks: -GFX_EXPORT skia::RefPtr<SkShader> CreateGradientShader(int start_point, - int end_point, - SkColor start_color, - SkColor end_color); +GFX_EXPORT sk_sp<SkShader> CreateGradientShader(int start_point, + int end_point, + SkColor start_color, + SkColor end_color); // Creates a draw looper to generate |shadows|. The caller owns the draw looper. // NULL is returned if |shadows| is empty since no draw looper is needed in // this case. -GFX_EXPORT skia::RefPtr<SkDrawLooper> CreateShadowDrawLooper( +GFX_EXPORT sk_sp<SkDrawLooper> CreateShadowDrawLooper( const std::vector<ShadowValue>& shadows); // Returns true if the two bitmaps contain the same pixels. @@ -88,6 +88,15 @@ GFX_EXPORT void ConvertSkiaToRGBA(const unsigned char* skia, int pixel_width, unsigned char* rgba); +// Converts a Skia floating-point value to an int appropriate for hb_position_t. +GFX_EXPORT int SkiaScalarToHarfBuzzUnits(SkScalar value); + +// Converts an hb_position_t to a Skia floating-point value. +GFX_EXPORT SkScalar HarfBuzzUnitsToSkiaScalar(int value); + +// Converts an hb_position_t to a float. +GFX_EXPORT float HarfBuzzUnitsToFloat(int value); + } // namespace gfx #endif // UI_GFX_SKIA_UTIL_H_ diff --git a/chromium/ui/gfx/sys_color_change_listener.cc b/chromium/ui/gfx/sys_color_change_listener.cc index a81409618e0..fcda18935af 100644 --- a/chromium/ui/gfx/sys_color_change_listener.cc +++ b/chromium/ui/gfx/sys_color_change_listener.cc @@ -20,16 +20,16 @@ bool g_is_inverted_color_scheme = false; bool g_is_inverted_color_scheme_initialized = false; void UpdateInvertedColorScheme() { - int foreground_luminance = color_utils::GetLuminanceForColor( - color_utils::GetSysSkColor(COLOR_WINDOWTEXT)); - int background_luminance = color_utils::GetLuminanceForColor( - color_utils::GetSysSkColor(COLOR_WINDOW)); + const uint8_t foreground_luma = + color_utils::GetLuma(color_utils::GetSysSkColor(COLOR_WINDOWTEXT)); + const uint8_t background_luma = + color_utils::GetLuma(color_utils::GetSysSkColor(COLOR_WINDOW)); HIGHCONTRAST high_contrast = {0}; high_contrast.cbSize = sizeof(HIGHCONTRAST); g_is_inverted_color_scheme = SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &high_contrast, 0) && ((high_contrast.dwFlags & HCF_HIGHCONTRASTON) != 0) && - foreground_luminance > background_luminance; + foreground_luma > background_luma; g_is_inverted_color_scheme_initialized = true; } diff --git a/chromium/ui/gfx/text_elider.cc b/chromium/ui/gfx/text_elider.cc index 4b7a7f9fc99..e8993d79877 100644 --- a/chromium/ui/gfx/text_elider.cc +++ b/chromium/ui/gfx/text_elider.cc @@ -259,10 +259,9 @@ base::string16 ElideText(const base::string16& text, } bool ElideString(const base::string16& input, - int max_len, + size_t max_len, base::string16* output) { - DCHECK_GE(max_len, 0); - if (static_cast<int>(input.length()) <= max_len) { + if (input.length() <= max_len) { output->assign(input); return false; } @@ -571,7 +570,7 @@ void RectangleText::AddString(const base::string16& input) { // The BREAK_NEWLINE iterator will keep the trailing newline character, // except in the case of the last line, which may not have one. Remove // the newline character, if it exists. - last_line_ended_in_lf_ = !line.empty() && line[line.length() - 1] == '\n'; + last_line_ended_in_lf_ = !line.empty() && line.back() == '\n'; if (last_line_ended_in_lf_) line.resize(line.length() - 1); AddLine(line); diff --git a/chromium/ui/gfx/text_elider.h b/chromium/ui/gfx/text_elider.h index 63f84c91b78..d7d54879989 100644 --- a/chromium/ui/gfx/text_elider.h +++ b/chromium/ui/gfx/text_elider.h @@ -96,7 +96,7 @@ GFX_EXPORT base::string16 ElideFilename(const base::FilePath& filename, // puts "Hell...Tom" in str and returns true. // TODO(tsepez): Doesn't handle UTF-16 surrogate pairs properly. // TODO(tsepez): Doesn't handle bidi properly. -GFX_EXPORT bool ElideString(const base::string16& input, int max_len, +GFX_EXPORT bool ElideString(const base::string16& input, size_t max_len, base::string16* output); // Reformat |input| into |output| so that it fits into a |max_rows| by diff --git a/chromium/ui/gfx/text_elider_unittest.cc b/chromium/ui/gfx/text_elider_unittest.cc index ce8c2f07245..4490110ccc3 100644 --- a/chromium/ui/gfx/text_elider_unittest.cc +++ b/chromium/ui/gfx/text_elider_unittest.cc @@ -594,7 +594,7 @@ TEST(TextEliderTest, StringSlicerCombiningSurrogate) { TEST(TextEliderTest, ElideString) { struct TestData { const char* input; - int max_len; + size_t max_len; bool result; const char* output; } cases[] = { @@ -831,7 +831,7 @@ TEST(TextEliderTest, MAYBE_ElideRectangleTextCheckLineWidth) { EXPECT_LE(GetStringWidthF(lines[1], font_list), kAvailableWidth); } -#ifdef OS_CHROMEOS +#if defined(OS_CHROMEOS) // This test was created specifically to test a message from crbug.com/415213. // It tests that width of concatenation of words equals sum of widths of the // words. @@ -846,7 +846,7 @@ TEST(TextEliderTest, ElideRectangleTextCheckConcatWidthEqualsSumOfWidths) { #undef WIDTH SetFontRenderParamsDeviceScaleFactor(1.0f); } -#endif // OS_CHROMEOS +#endif // defined(OS_CHROMEOS) // TODO(crbug.com/338784): Enable this on android. #if defined(OS_ANDROID) diff --git a/chromium/ui/gfx/vector_icons/aggregate_vector_icons.py b/chromium/ui/gfx/vector_icons/aggregate_vector_icons.py index 72fbc11cc93..938f1a858fd 100644 --- a/chromium/ui/gfx/vector_icons/aggregate_vector_icons.py +++ b/chromium/ui/gfx/vector_icons/aggregate_vector_icons.py @@ -9,6 +9,10 @@ import os import textwrap +# TODO(brettw) bug 535386: This should not take a directory as an input, but +# rather a response file listing the inputs or sometimes the build will be +# incorrect. + def AggregateVectorIcons(working_directory, output_cc, output_h): """Compiles all .icon files in a directory into two C++ files. diff --git a/chromium/ui/gfx/vector_icons/autofill.icon b/chromium/ui/gfx/vector_icons/autofill.icon deleted file mode 100644 index 811335064ba..00000000000 --- a/chromium/ui/gfx/vector_icons/autofill.icon +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -MOVE_TO, 6, 34.5f, -LINE_TO, 6, 42, -LINE_TO, 13.5f, 42, -LINE_TO, 35.63f, 19.87f, -LINE_TO, 28.13f, 12.37f, -LINE_TO, 6, 34.5f, -LINE_TO, 6, 34.5f, -CLOSE, -MOVE_TO, 41.41f, 14.09f, -CUBIC_TO, 42.2f, 13.3f, 42.2f, 12.04f, 41.41f, 11.26f, -LINE_TO, 36.74f, 6.59f, -CUBIC_TO, 35.96f, 5.8f, 34.7f, 5.8f, 33.91f, 6.59f, -LINE_TO, 30.25f, 10.25f, -LINE_TO, 37.75f, 17.75f, -LINE_TO, 41.41f, 14.09f, -LINE_TO, 41.41f, 14.09f, -CLOSE, -MOVE_TO, 24, 38, -LINE_TO, 20, 42, -LINE_TO, 42, 42, -LINE_TO, 42, 38, -LINE_TO, 24, 38, -LINE_TO, 24, 38, -CLOSE, -END diff --git a/chromium/ui/gfx/vector_icons/combobox_arrow_mac.icon b/chromium/ui/gfx/vector_icons/combobox_arrow_mac.icon new file mode 100644 index 00000000000..4f5858287b2 --- /dev/null +++ b/chromium/ui/gfx/vector_icons/combobox_arrow_mac.icon @@ -0,0 +1,13 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +STROKE, 4, +MOVE_TO, 8, 10, +LINE_TO, 16, 2, +LINE_TO, 24, 10, +MOVE_TO, 8, 22, +LINE_TO, 16, 30, +LINE_TO, 24, 22, +END diff --git a/chromium/ui/gfx/vector_icons/menu_radio_empty.icon b/chromium/ui/gfx/vector_icons/menu_radio_empty.icon new file mode 100644 index 00000000000..62ef2daec82 --- /dev/null +++ b/chromium/ui/gfx/vector_icons/menu_radio_empty.icon @@ -0,0 +1,8 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +CIRCLE, 16, 16, 14, +CIRCLE, 16, 16, 11, +END diff --git a/chromium/ui/gfx/vector_icons/menu_radio_selected.icon b/chromium/ui/gfx/vector_icons/menu_radio_selected.icon new file mode 100644 index 00000000000..0533b99ba7e --- /dev/null +++ b/chromium/ui/gfx/vector_icons/menu_radio_selected.icon @@ -0,0 +1,9 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +CIRCLE, 16, 16, 14, +CIRCLE, 16, 16, 11, +CIRCLE, 16, 16, 7, +END diff --git a/chromium/ui/gfx/vector_icons/tab_bluetooth_connected.icon b/chromium/ui/gfx/vector_icons/tab_bluetooth_connected.icon new file mode 100644 index 00000000000..9ed11250a58 --- /dev/null +++ b/chromium/ui/gfx/vector_icons/tab_bluetooth_connected.icon @@ -0,0 +1,34 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 16.8f, 2, +R_H_LINE_TO, -1.4f, +R_V_LINE_TO, 10.63f, +LINE_TO, 8.97f, 6.2f, +LINE_TO, 7, 8.17f, +LINE_TO, 14.83f, 16, +LINE_TO, 7, 23.83f, +LINE_TO, 8.97f, 25.8f, +R_LINE_TO, 6.43f, -6.43f, +V_LINE_TO, 30, +R_H_LINE_TO, 1.4f, +R_LINE_TO, 7.99f, -7.99f, +LINE_TO, 18.77f, 16, +R_LINE_TO, 6.02f, -6.01f, +LINE_TO, 16.8f, 2, +CLOSE, +MOVE_TO, 18, 7, +R_LINE_TO, 3, 3, +R_LINE_TO, -3, 3, +R_LINE_TO, 0.2f, -5.64f, +LINE_TO, 18, 7, +CLOSE, +R_MOVE_TO, 3, 15.01f, +LINE_TO, 18, 25, +R_V_LINE_TO, -6, +R_LINE_TO, 2.83f, 3.01f, +H_LINE_TO, 21, +CLOSE, +END
\ No newline at end of file diff --git a/chromium/ui/gfx/vector_icons/tab_usb_connected.icon b/chromium/ui/gfx/vector_icons/tab_usb_connected.icon new file mode 100644 index 00000000000..c383dca1eb0 --- /dev/null +++ b/chromium/ui/gfx/vector_icons/tab_usb_connected.icon @@ -0,0 +1,39 @@ +// 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. + +CANVAS_DIMENSIONS, 24, +MOVE_TO, 15, 7, +R_V_LINE_TO, 4, +R_H_LINE_TO, 1, +R_V_LINE_TO, 2, +R_H_LINE_TO, -3, +V_LINE_TO, 5, +R_H_LINE_TO, 2, +R_LINE_TO, -3, -4, +R_LINE_TO, -3, 4, +R_H_LINE_TO, 2, +R_V_LINE_TO, 8, +H_LINE_TO, 8, +R_V_LINE_TO, -2.07f, +R_CUBIC_TO, 0.7f, -0.37f, 1.2f, -1.08f, 1.2f, -1.93f, +R_CUBIC_TO, 0, -1.21f, -0.99f, -2.2f, -2.2f, -2.2f, +R_CUBIC_TO, -1.21f, 0, -2.2f, 0.99f, -2.2f, 2.2f, +R_CUBIC_TO, 0, 0.85f, 0.5f, 1.56f, 1.2f, 1.93f, +V_LINE_TO, 13, +R_CUBIC_TO, 0, 1.11f, 0.89f, 2, 2, 2, +R_H_LINE_TO, 3, +R_V_LINE_TO, 3.05f, +R_CUBIC_TO, -0.71f, 0.37f, -1.2f, 1.1f, -1.2f, 1.95f, +R_CUBIC_TO, 0, 1.22f, 0.99f, 2.2f, 2.2f, 2.2f, +R_CUBIC_TO, 1.21f, 0, 2.2f, -0.98f, 2.2f, -2.2f, +R_CUBIC_TO, 0, -0.85f, -0.49f, -1.58f, -1.2f, -1.95f, +V_LINE_TO, 15, +R_H_LINE_TO, 3, +R_CUBIC_TO, 1.11f, 0, 2, -0.89f, 2, -2, +R_V_LINE_TO, -2, +R_H_LINE_TO, 1, +V_LINE_TO, 7, +R_H_LINE_TO, -4, +CLOSE, +END diff --git a/chromium/ui/gfx/vector_icons/translate.icon b/chromium/ui/gfx/vector_icons/translate.icon index 620036f4c7d..d553c9bab1f 100644 --- a/chromium/ui/gfx/vector_icons/translate.icon +++ b/chromium/ui/gfx/vector_icons/translate.icon @@ -3,66 +3,73 @@ // found in the LICENSE file. CANVAS_DIMENSIONS, 16, -MOVE_TO, 7.7f, 3.8f, -H_LINE_TO, 6.7f, -CUBIC_TO, 6.3f, 5.3f, 6, 5.6f, 5.5f, 6.3f, -R_CUBIC_TO, 0.6f, 0.4f, 1.4f, 0.9f, 2.1f, 1.2f, -LINE_TO, 7.2f, 8.1f, -CUBIC_TO, 6.3f, 7.8f, 5.8f, 7.3f, 5, 6.8f, -R_CUBIC_TO, -0.8f, 0.5f, -1.3f, 1, -2.2f, 1.3f, -LINE_TO, 2.3f, 7.5f, -R_CUBIC_TO, 0.8f, -0.3f, 1.6f, -0.8f, 2.1f, -1.2f, -CUBIC_TO, 4, 5.6f, 3.7f, 5.2f, 3.4f, 4.5f, -R_H_LINE_TO, 0.7f, -CUBIC_TO, 4.4f, 5, 4.7f, 5.5f, 5, 5.9f, -R_CUBIC_TO, 0.5f, -0.6f, 0.9f, -1.1f, 1, -2.2f, -R_LINE_TO, -3.7f, 0, -V_LINE_TO, 3.1f, -R_H_LINE_TO, 2.3f, -V_LINE_TO, 2.3f, -R_H_LINE_TO, 0.7f, -R_V_LINE_TO, 0.7f, -R_H_LINE_TO, 2.3f, -V_LINE_TO, 3.8f, +MOVE_TO, 13.6f, 3.1f, +LINE_TO, 7.22f, 3.1f, +LINE_TO, 6.6f, 1, +LINE_TO, 2.4f, 1, +CUBIC_TO, 1.63f, 1, 1, 1.63f, 1, 2.4f, +LINE_TO, 1, 11.5f, +CUBIC_TO, 1, 12.27f, 1.63f, 12.9f, 2.4f, 12.9f, +LINE_TO, 7.3f, 12.9f, +LINE_TO, 8, 15, +LINE_TO, 13.6f, 15, +CUBIC_TO, 14.37f, 15, 15, 14.37f, 15, 13.6f, +LINE_TO, 15, 4.5f, +CUBIC_TO, 15, 3.73f, 14.37f, 3.1f, 13.6f, 3.1f, +LINE_TO, 13.6f, 3.1f, CLOSE, -MOVE_TO, 7.7f, 1, -H_LINE_TO, 2.3f, -CUBIC_TO, 1.7f, 1, 1, 1.7f, 1, 2.3f, -R_V_LINE_TO, 5.3f, -CUBIC_TO, 1, 8.2f, 1.8f, 9, 2.3f, 9, -R_H_LINE_TO, 5.3f, -CUBIC_TO, 8.2f, 9, 9, 8.2f, 9, 7.7f, -V_LINE_TO, 2.3f, -CUBIC_TO, 9, 1.7f, 8.3f, 1, 7.7f, 1, +MOVE_TO, 4.62f, 9.81f, +CUBIC_TO, 3.04f, 9.81f, 1.76f, 8.53f, 1.76f, 6.95f, +CUBIC_TO, 1.76f, 5.37f, 3.04f, 4.09f, 4.62f, 4.09f, +CUBIC_TO, 5.35f, 4.09f, 6.01f, 4.35f, 6.54f, 4.84f, +LINE_TO, 6.59f, 4.88f, +LINE_TO, 5.73f, 5.7f, +LINE_TO, 5.68f, 5.67f, +CUBIC_TO, 5.48f, 5.48f, 5.14f, 5.26f, 4.62f, 5.26f, +CUBIC_TO, 3.7f, 5.26f, 2.95f, 6.02f, 2.95f, 6.95f, +CUBIC_TO, 2.95f, 7.88f, 3.7f, 8.64f, 4.62f, 8.64f, +CUBIC_TO, 5.58f, 8.64f, 5.99f, 8.04f, 6.1f, 7.62f, +LINE_TO, 4.56f, 7.62f, +LINE_TO, 4.56f, 6.54f, +LINE_TO, 7.32f, 6.54f, +LINE_TO, 7.33f, 6.59f, +CUBIC_TO, 7.36f, 6.73f, 7.36f, 6.87f, 7.36f, 7.01f, +CUBIC_TO, 7.36f, 8.66f, 6.24f, 9.81f, 4.62f, 9.81f, +LINE_TO, 4.62f, 9.81f, CLOSE, -NEW_PATH, -MOVE_TO, 13.7f, 7, -H_LINE_TO, 9, -H_LINE_TO, 8, -R_V_LINE_TO, 1, -R_V_LINE_TO, 0.5f, -H_LINE_TO, 7, -V_LINE_TO, 9, -R_V_LINE_TO, 1, -R_V_LINE_TO, 3.7f, -CUBIC_TO, 7, 14.3f, 7.7f, 15, 8.3f, 15, -R_H_LINE_TO, 5.6f, -R_CUBIC_TO, 0.6f, 0, 1.1f, -0.6f, 1.1f, -1.1f, -V_LINE_TO, 8.3f, -CUBIC_TO, 15, 7.7f, 14.3f, 7, 13.7f, 7, +MOVE_TO, 8.84f, 8.62f, +CUBIC_TO, 9.07f, 9.04f, 9.36f, 9.44f, 9.67f, 9.81f, +LINE_TO, 9.3f, 10.18f, +LINE_TO, 8.84f, 8.62f, +LINE_TO, 8.84f, 8.62f, CLOSE, -MOVE_TO, 12.4f, 13.2f, -R_LINE_TO, -0.4f, -1, -H_LINE_TO, 10, -R_LINE_TO, -0.4f, 1, -H_LINE_TO, 8.8f, -R_LINE_TO, 1.7f, -4.4f, -R_H_LINE_TO, 1, -R_LINE_TO, 1.7f, 4.4f, -H_LINE_TO, 12.4f, +MOVE_TO, 13.6f, 14.3f, +LINE_TO, 8.7f, 14.3f, +LINE_TO, 10.1f, 12.9f, +LINE_TO, 9.53f, 10.96f, +LINE_TO, 10.18f, 10.32f, +LINE_TO, 12.05f, 12.2f, +LINE_TO, 12.56f, 11.69f, +LINE_TO, 10.67f, 9.81f, +CUBIC_TO, 11.3f, 9.09f, 11.79f, 8.24f, 12.01f, 7.36f, +LINE_TO, 12.9f, 7.36f, +LINE_TO, 12.9f, 6.63f, +LINE_TO, 10.35f, 6.63f, +LINE_TO, 10.35f, 5.9f, +LINE_TO, 9.62f, 5.9f, +LINE_TO, 9.62f, 6.63f, +LINE_TO, 8.25f, 6.63f, +LINE_TO, 7.43f, 3.8f, +LINE_TO, 13.6f, 3.8f, +CUBIC_TO, 13.99f, 3.8f, 14.3f, 4.12f, 14.3f, 4.5f, +LINE_TO, 14.3f, 13.6f, +CUBIC_TO, 14.3f, 13.99f, 13.99f, 14.3f, 13.6f, 14.3f, CLOSE, -MOVE_TO, 10.1f, 11.4f, -LINE_TO, 11.9f, 11.4f, -LINE_TO, 11, 9.2f, +MOVE_TO, 8.69f, 8.08f, +LINE_TO, 8.47f, 7.36f, +LINE_TO, 11.26f, 7.36f, +CUBIC_TO, 11.26f, 7.36f, 11.02f, 8.27f, 10.17f, 9.27f, +CUBIC_TO, 9.81f, 8.84f, 9.55f, 8.41f, 9.38f, 8.08f, +LINE_TO, 8.69f, 8.08f, CLOSE, END diff --git a/chromium/ui/gfx/vector_icons/upgrade_menu_item.icon b/chromium/ui/gfx/vector_icons/upgrade_menu_item.icon new file mode 100644 index 00000000000..dcf04ed9aa0 --- /dev/null +++ b/chromium/ui/gfx/vector_icons/upgrade_menu_item.icon @@ -0,0 +1,16 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +ROUND_RECT, 2, 2, 28, 28, 4, +MOVE_TO, 12, 24, +R_H_LINE_TO, 8, +R_V_LINE_TO, -6, +R_H_LINE_TO, 4, +LINE_TO, 16, 8, +LINE_TO, 8, 18, +R_H_LINE_TO, 4, +R_V_LINE_TO, 6, +CLOSE, +END diff --git a/chromium/ui/gfx/win/direct_write.cc b/chromium/ui/gfx/win/direct_write.cc index 71a023f316b..bf8c881d946 100644 --- a/chromium/ui/gfx/win/direct_write.cc +++ b/chromium/ui/gfx/win/direct_write.cc @@ -44,11 +44,8 @@ bool ShouldUseDirectWrite() { } void CreateDWriteFactory(IDWriteFactory** factory) { - if (!ShouldUseDirectWrite() || - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableDirectWriteForUI)) { + if (!ShouldUseDirectWrite()) return; - } using DWriteCreateFactoryProc = decltype(DWriteCreateFactory)*; HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); @@ -78,6 +75,11 @@ void MaybeInitializeDirectWrite() { return; tried_dwrite_initialize = true; + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableDirectWriteForUI)) { + return; + } + base::win::ScopedComPtr<IDWriteFactory> factory; CreateDWriteFactory(factory.Receive()); diff --git a/chromium/ui/gfx/win/physical_size.cc b/chromium/ui/gfx/win/physical_size.cc new file mode 100644 index 00000000000..2aa0c3fd892 --- /dev/null +++ b/chromium/ui/gfx/win/physical_size.cc @@ -0,0 +1,163 @@ +// 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/win/physical_size.h" + +#include <windows.h> +#include <setupapi.h> + +#include <iostream> + +#include "base/logging.h" +#include "base/memory/free_deleter.h" +#include "base/memory/scoped_ptr.h" +#include "base/scoped_generic.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/registry.h" + +// This GUID {E6F07B5F-EE97-4A90-B076-33F57BF4EAA7} was taken from +// https://msdn.microsoft.com/en-us/library/windows/hardware/ff545901.aspx +const GUID GUID_DEVICEINTERFACE_MONITOR = { + 0xE6F07B5F, + 0xEE97, + 0x4A90, + {0xB0, 0x76, 0x33, 0xF5, 0x7B, 0xF4, 0xEA, 0xA7}}; + +namespace { + +struct DeviceInfoListScopedTraits { + static HDEVINFO InvalidValue() { return INVALID_HANDLE_VALUE; } + + static void Free(HDEVINFO h) { SetupDiDestroyDeviceInfoList(h); } +}; + +bool GetSizeFromRegistry(HDEVINFO device_info_list, + SP_DEVINFO_DATA* device_info, + int* width_mm, + int* height_mm) { + base::win::RegKey reg_key(SetupDiOpenDevRegKey( + device_info_list, device_info, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ)); + if (!reg_key.Valid()) + return false; + + BYTE data[128]; // EDID block is exactly 128 bytes long. + ZeroMemory(&data[0], sizeof(data)); + DWORD data_length = sizeof(data); + LONG return_value = + reg_key.ReadValue(L"EDID", &data[0], &data_length, nullptr); + if (return_value != ERROR_SUCCESS) + return false; + + // Byte 54 is the start of the first descriptor block, which contains the + // required timing information with the highest preference, and 12 bytes + // into that block is the size information. + // 66: width least significant bits + // 67: height least significant bits + // 68: 4 bits for each of width and height most significant bits + if (data[54] == 0) + return false; + const int w = ((data[68] & 0xF0) << 4) + data[66]; + const int h = ((data[68] & 0x0F) << 8) + data[67]; + + if (w <= 0 || h <= 0) + return false; + + *width_mm = w; + *height_mm = h; + + return true; +} + +bool GetInterfaceDetailAndDeviceInfo( + HDEVINFO device_info_list, + SP_DEVICE_INTERFACE_DATA* interface_data, + scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>* + interface_detail, + SP_DEVINFO_DATA* device_info) { + DCHECK_EQ(sizeof(*device_info), device_info->cbSize); + DWORD buffer_size; + // This call populates device_info. It will also fail, but if the error is + // "insufficient buffer" then it will set buffer_size and we can call again + // with an allocated buffer. + SetupDiGetDeviceInterfaceDetail(device_info_list, interface_data, nullptr, 0, + &buffer_size, device_info); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return false; + + interface_detail->reset( + reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(buffer_size))); + (*interface_detail)->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + return SetupDiGetDeviceInterfaceDetail(device_info_list, interface_data, + interface_detail->get(), buffer_size, + nullptr, nullptr) != 0; +} + +} // namespace + +namespace gfx { + +// The physical size information is only available by looking in the EDID block +// via setup. However setup has the device path and not the device name that we +// use to identify displays. Therefore after looking up a device via setup we +// need to find the display again via EnumDisplayDevices (matching device path +// to the device ID of the display's interface) so we can return the device name +// (available from the interface's attached monitor). +std::vector<PhysicalDisplaySize> GetPhysicalSizeForDisplays() { + std::vector<PhysicalDisplaySize> out; + + base::ScopedGeneric<HDEVINFO, DeviceInfoListScopedTraits> device_info_list( + SetupDiGetClassDevs(&GUID_DEVICEINTERFACE_MONITOR, nullptr, nullptr, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); + + if (!device_info_list.is_valid()) + return out; + + SP_DEVICE_INTERFACE_DATA interface_data = {}; + interface_data.cbSize = sizeof(interface_data); + int interface_index = 0; + while (SetupDiEnumDeviceInterfaces(device_info_list.get(), nullptr, + &GUID_DEVICEINTERFACE_MONITOR, + interface_index++, &interface_data)) { + scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter> + interface_detail; + SP_DEVINFO_DATA device_info = {}; + device_info.cbSize = sizeof(device_info); + bool get_info_succeeded = + GetInterfaceDetailAndDeviceInfo(device_info_list.get(), &interface_data, + &interface_detail, &device_info); + if (!get_info_succeeded) + continue; + + DISPLAY_DEVICE display_device = {}; + display_device.cb = sizeof(display_device); + int display_index = 0; + while (EnumDisplayDevices(nullptr, display_index++, &display_device, + EDD_GET_DEVICE_INTERFACE_NAME)) { + DISPLAY_DEVICE attached_device = {}; + attached_device.cb = sizeof(attached_device); + int attached_index = 0; + while (EnumDisplayDevices(display_device.DeviceName, attached_index++, + &attached_device, + EDD_GET_DEVICE_INTERFACE_NAME)) { + wchar_t* attached_device_id = attached_device.DeviceID; + wchar_t* setup_device_path = interface_detail->DevicePath; + if (wcsicmp(attached_device_id, setup_device_path) == 0) { + int width_mm; + int height_mm; + bool found = GetSizeFromRegistry(device_info_list.get(), &device_info, + &width_mm, &height_mm); + if (found) { + out.push_back( + PhysicalDisplaySize(base::WideToUTF8(display_device.DeviceName), + width_mm, height_mm)); + } + break; + } + } + } + } + return out; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/win/physical_size.h b/chromium/ui/gfx/win/physical_size.h new file mode 100644 index 00000000000..10d94714ffb --- /dev/null +++ b/chromium/ui/gfx/win/physical_size.h @@ -0,0 +1,31 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_WIN_PHYSICAL_SIZE_H_ +#define UI_GFX_WIN_PHYSICAL_SIZE_H_ + +#include <string> +#include <vector> + +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +struct PhysicalDisplaySize { + PhysicalDisplaySize(const std::string& display_name, + int width_mm, + int height_mm) + : display_name(display_name), width_mm(width_mm), height_mm(height_mm) {} + + std::string display_name; + int width_mm; + int height_mm; +}; + +// Gets the physical size for all displays. +GFX_EXPORT std::vector<PhysicalDisplaySize> GetPhysicalSizeForDisplays(); + +} // namespace gfx + +#endif // UI_GFX_DPI_WIN_H_ diff --git a/chromium/ui/gfx/win/rendering_window_manager.cc b/chromium/ui/gfx/win/rendering_window_manager.cc new file mode 100644 index 00000000000..a0528ecf9ab --- /dev/null +++ b/chromium/ui/gfx/win/rendering_window_manager.cc @@ -0,0 +1,70 @@ +// 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/win/rendering_window_manager.h" + +#include "base/memory/singleton.h" + +namespace gfx { + +// static +RenderingWindowManager* RenderingWindowManager::GetInstance() { + return base::Singleton<RenderingWindowManager>::get(); +} + +void RenderingWindowManager::RegisterParent(HWND parent) { + base::AutoLock lock(lock_); + + info_[parent] = nullptr; +} + +bool RenderingWindowManager::RegisterChild(HWND parent, HWND child_window) { + if (!child_window) + return false; + + base::AutoLock lock(lock_); + + auto it = info_.find(parent); + if (it == info_.end()) + return false; + if (it->second) + return false; + + info_[parent] = child_window; + return true; +} + +void RenderingWindowManager::DoSetParentOnChild(HWND parent) { + HWND child; + { + base::AutoLock lock(lock_); + + auto it = info_.find(parent); + if (it == info_.end()) + return; + if (!it->second) + return; + child = it->second; + } + + ::SetParent(child, parent); +} + +void RenderingWindowManager::UnregisterParent(HWND parent) { + base::AutoLock lock(lock_); + info_.erase(parent); +} + +bool RenderingWindowManager::HasValidChildWindow(HWND parent) { + base::AutoLock lock(lock_); + auto it = info_.find(parent); + if (it == info_.end()) + return false; + return !!it->second && ::IsWindow(it->second); +} + +RenderingWindowManager::RenderingWindowManager() {} +RenderingWindowManager::~RenderingWindowManager() {} + +} // namespace gfx diff --git a/chromium/ui/gfx/win/rendering_window_manager.h b/chromium/ui/gfx/win/rendering_window_manager.h new file mode 100644 index 00000000000..bbb74ac8d22 --- /dev/null +++ b/chromium/ui/gfx/win/rendering_window_manager.h @@ -0,0 +1,48 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_WIN_RENDERING_WINDOW_MANAGER_H_ +#define UI_GFX_WIN_RENDERING_WINDOW_MANAGER_H_ + +#include <windows.h> + +#include <map> + +#include "base/synchronization/lock.h" +#include "ui/gfx/gfx_export.h" + +namespace base { +template <typename T> +struct DefaultSingletonTraits; +} + +namespace gfx { + +// This keeps track of whether a given HWND has a child window which the GPU +// process renders into. +class GFX_EXPORT RenderingWindowManager { + public: + static RenderingWindowManager* GetInstance(); + + void RegisterParent(HWND parent); + bool RegisterChild(HWND parent, HWND child_window); + void DoSetParentOnChild(HWND parent); + void UnregisterParent(HWND parent); + bool HasValidChildWindow(HWND parent); + + private: + friend struct base::DefaultSingletonTraits<RenderingWindowManager>; + + RenderingWindowManager(); + ~RenderingWindowManager(); + + base::Lock lock_; + std::map<HWND, HWND> info_; + + DISALLOW_COPY_AND_ASSIGN(RenderingWindowManager); +}; + +} // namespace gfx + +#endif // UI_GFX_WIN_RENDERING_WINDOW_MANAGER_H_ diff --git a/chromium/ui/gl/BUILD.gn b/chromium/ui/gl/BUILD.gn index eb46bd29707..9d92df53943 100644 --- a/chromium/ui/gl/BUILD.gn +++ b/chromium/ui/gl/BUILD.gn @@ -34,8 +34,6 @@ component("gl") { "gl_bindings_autogen_gl.h", "gl_bindings_autogen_osmesa.cc", "gl_bindings_autogen_osmesa.h", - "gl_bindings_skia_in_process.cc", - "gl_bindings_skia_in_process.h", "gl_context.cc", "gl_context.h", "gl_context_android.cc", @@ -116,7 +114,7 @@ component("gl") { defines = [ "GL_IMPLEMENTATION" ] include_dirs = [ - "//third_party/switfshader/include", + "//third_party/swiftshader/include", "//third_party/mesa/src/include", ] @@ -126,7 +124,6 @@ component("gl") { deps = [ "//base/third_party/dynamic_annotations", - "//skia", ] public_deps = [ "//base", @@ -191,8 +188,8 @@ component("gl") { deps += [ "//ui/gfx/x" ] data_deps = [ - "//third_party/angle:libEGL_ANGLE", - "//third_party/angle:libGLESv2_ANGLE", + "//third_party/angle:libEGL", + "//third_party/angle:libGLESv2", ] } if (is_win) { @@ -270,10 +267,7 @@ component("gl") { "gl_implementation_ozone.cc", "gl_surface_ozone.cc", ] - deps += [ - "//ui/ozone", - "//ui/ozone:ozone_base", - ] + deps += [ "//ui/ozone" ] } } @@ -328,15 +322,6 @@ source_set("test_support") { } } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("gl_unittests_run") { - testonly = true - deps = [ - ":gl_unittests", - ] -} - test("gl_unittests") { sources = [ "gl_api_unittest.cc", diff --git a/chromium/ui/gl/OWNERS b/chromium/ui/gl/OWNERS index f8419dc458e..1c3ad17be25 100644 --- a/chromium/ui/gl/OWNERS +++ b/chromium/ui/gl/OWNERS @@ -1,7 +1,7 @@ kbr@chromium.org piman@chromium.org sievers@chromium.org -per-file gl_image*=reveman@chromium.org +per-file *gl_image*=reveman@chromium.org per-file *_ozone*=alexst@chromium.org per-file *_ozone*=dnicoara@chromium.org per-file *_ozone*=spang@chromium.org diff --git a/chromium/ui/gl/angle_platform_impl.cc b/chromium/ui/gl/angle_platform_impl.cc index d87f7610170..84c6a3c44c0 100644 --- a/chromium/ui/gl/angle_platform_impl.cc +++ b/chromium/ui/gl/angle_platform_impl.cc @@ -52,7 +52,8 @@ angle::Platform::TraceEventHandle ANGLEPlatformImpl::addTraceEvent( base::TimeTicks() + base::TimeDelta::FromSecondsD(timestamp); base::trace_event::TraceEventHandle handle = TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP( - phase, category_group_enabled, name, id, trace_event_internal::kNoId, + phase, category_group_enabled, name, + 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::Platform::TraceEventHandle result; diff --git a/chromium/ui/gl/generate_bindings.py b/chromium/ui/gl/generate_bindings.py index 1e0a629863b..9af3179cbed 100755 --- a/chromium/ui/gl/generate_bindings.py +++ b/chromium/ui/gl/generate_bindings.py @@ -880,6 +880,9 @@ GL_FUNCTIONS = [ 'names': ['glPopGroupMarkerEXT'], 'arguments': 'void', }, { 'return_type': 'void', + 'names': ['glPrimitiveRestartIndex'], + 'arguments': 'GLuint index', }, +{ 'return_type': 'void', 'known_as': 'glProgramBinary', 'versions': [{ 'name': 'glProgramBinaryOES' }, { 'name': 'glProgramBinary', @@ -1660,16 +1663,16 @@ GLX_FUNCTIONS = [ { 'return_type': 'bool', 'names': ['glXGetMscRateOML'], 'arguments': - 'Display* dpy, GLXDrawable drawable, int32* numerator, ' - 'int32* denominator' }, + 'Display* dpy, GLXDrawable drawable, int32_t* numerator, ' + 'int32_t* denominator' }, { 'return_type': 'void', 'names': ['glXGetSelectedEvent'], 'arguments': 'Display* dpy, GLXDrawable drawable, unsigned long* mask', }, { 'return_type': 'bool', 'names': ['glXGetSyncValuesOML'], 'arguments': - 'Display* dpy, GLXDrawable drawable, int64* ust, int64* msc, ' - 'int64* sbc' }, + 'Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, ' + 'int64_t* sbc' }, { 'return_type': 'XVisualInfo*', 'names': ['glXGetVisualFromFBConfig'], 'arguments': 'Display* dpy, GLXFBConfig config', }, @@ -1742,7 +1745,10 @@ FUNCTION_SETS = [ 'GL/glextchromium.h', 'GLES2/gl2chromium.h', 'GLES2/gl2extchromium.h' - ], []], + ], [ + "GL_EXT_unpack_subimage", + ] + ], [OSMESA_FUNCTIONS, 'osmesa', [], []], [EGL_FUNCTIONS, 'egl', [ 'EGL/eglext.h', @@ -2171,6 +2177,8 @@ void Driver%s::InitializeExtensionBindings() { if return_type == 'void': file.write(' GL_SERVICE_LOG("%s" << "(" %s << ")");\n' % (function_name, log_argument_names)) + file.write(' DCHECK(g_driver_%s.debug_fn.%sFn != nullptr);\n' % + (set_name.lower(), function_name)) file.write(' g_driver_%s.debug_fn.%sFn(%s);\n' % (set_name.lower(), function_name, argument_names)) if 'logging_code' in func: @@ -2183,6 +2191,8 @@ void Driver%s::InitializeExtensionBindings() { else: file.write(' GL_SERVICE_LOG("%s" << "(" %s << ")");\n' % (function_name, log_argument_names)) + file.write(' DCHECK(g_driver_%s.debug_fn.%sFn != nullptr);\n' % + (set_name.lower(), function_name)) file.write(' %s result = g_driver_%s.debug_fn.%sFn(%s);\n' % (return_type, set_name.lower(), function_name, argument_names)) if 'logging_code' in func: diff --git a/chromium/ui/gl/gl.gyp b/chromium/ui/gl/gl.gyp index afa2ad0fb5a..9dfd8ed963d 100644 --- a/chromium/ui/gl/gl.gyp +++ b/chromium/ui/gl/gl.gyp @@ -47,8 +47,6 @@ 'gl_bindings_autogen_gl.h', 'gl_bindings_autogen_osmesa.cc', 'gl_bindings_autogen_osmesa.h', - 'gl_bindings_skia_in_process.cc', - 'gl_bindings_skia_in_process.h', 'gl_context.cc', 'gl_context.h', 'gl_context_android.cc', @@ -186,10 +184,16 @@ '<(DEPTH)/build/linux/system.gyp:xcomposite', '<(DEPTH)/build/linux/system.gyp:xext', '<(DEPTH)/ui/events/platform/events_platform.gyp:events_platform', - '<(DEPTH)/third_party/angle/src/angle.gyp:libEGL_ANGLE', - '<(DEPTH)/third_party/angle/src/angle.gyp:libGLESv2_ANGLE', '<(DEPTH)/ui/gfx/x/gfx_x11.gyp:gfx_x11', ], + 'copies': [{ + 'destination': '<(PRODUCT_DIR)', + 'files': + [ + "<(PRODUCT_DIR)/lib/libEGL.so", + "<(PRODUCT_DIR)/lib/libGLESv2.so", + ], + }], }], ['OS=="win"', { 'sources': [ diff --git a/chromium/ui/gl/gl_bindings.cc b/chromium/ui/gl/gl_bindings.cc index bb700c34740..0f47871411d 100644 --- a/chromium/ui/gl/gl_bindings.cc +++ b/chromium/ui/gl/gl_bindings.cc @@ -11,7 +11,7 @@ #include "ui/gl/gl_bindings.h" #if defined(USE_X11) -#include "ui/gfx/x/x11_types.h" +#include "ui/gfx/x/x11_types.h" // nogncheck #endif #if defined(OS_WIN) diff --git a/chromium/ui/gl/gl_bindings_api_autogen_gl.h b/chromium/ui/gl/gl_bindings_api_autogen_gl.h index 94d4ed8355f..a513ed20942 100644 --- a/chromium/ui/gl/gl_bindings_api_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_api_autogen_gl.h @@ -526,6 +526,7 @@ void glPixelStoreiFn(GLenum pname, GLint param) override; void glPointParameteriFn(GLenum pname, GLint param) override; void glPolygonOffsetFn(GLfloat factor, GLfloat units) override; void glPopGroupMarkerEXTFn(void) override; +void glPrimitiveRestartIndexFn(GLuint index) override; void glProgramBinaryFn(GLuint program, GLenum binaryFormat, const GLvoid* binary, diff --git a/chromium/ui/gl/gl_bindings_autogen_egl.cc b/chromium/ui/gl/gl_bindings_autogen_egl.cc index 2bb0bfd8e96..c2461d8ed9b 100644 --- a/chromium/ui/gl/gl_bindings_autogen_egl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_egl.cc @@ -203,6 +203,7 @@ extern "C" { static EGLBoolean GL_BINDING_CALL Debug_eglBindAPI(EGLenum api) { GL_SERVICE_LOG("eglBindAPI" << "(" << api << ")"); + DCHECK(g_driver_egl.debug_fn.eglBindAPIFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglBindAPIFn(api); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -213,6 +214,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglBindTexImage(EGLDisplay dpy, EGLint buffer) { GL_SERVICE_LOG("eglBindTexImage" << "(" << dpy << ", " << surface << ", " << buffer << ")"); + DCHECK(g_driver_egl.debug_fn.eglBindTexImageFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglBindTexImageFn(dpy, surface, buffer); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -230,6 +232,7 @@ Debug_eglChooseConfig(EGLDisplay dpy, << ", " << static_cast<const void*>(configs) << ", " << config_size << ", " << static_cast<const void*>(num_config) << ")"); + DCHECK(g_driver_egl.debug_fn.eglChooseConfigFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglChooseConfigFn( dpy, attrib_list, configs, config_size, num_config); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -243,6 +246,7 @@ static EGLint GL_BINDING_CALL Debug_eglClientWaitSyncKHR(EGLDisplay dpy, GL_SERVICE_LOG("eglClientWaitSyncKHR" << "(" << dpy << ", " << sync << ", " << flags << ", " << timeout << ")"); + DCHECK(g_driver_egl.debug_fn.eglClientWaitSyncKHRFn != nullptr); EGLint result = g_driver_egl.debug_fn.eglClientWaitSyncKHRFn(dpy, sync, flags, timeout); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -255,6 +259,7 @@ Debug_eglCopyBuffers(EGLDisplay dpy, EGLNativePixmapType target) { GL_SERVICE_LOG("eglCopyBuffers" << "(" << dpy << ", " << surface << ", " << target << ")"); + DCHECK(g_driver_egl.debug_fn.eglCopyBuffersFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglCopyBuffersFn(dpy, surface, target); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -269,6 +274,7 @@ Debug_eglCreateContext(EGLDisplay dpy, GL_SERVICE_LOG("eglCreateContext" << "(" << dpy << ", " << config << ", " << share_context << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglCreateContextFn != nullptr); EGLContext result = g_driver_egl.debug_fn.eglCreateContextFn( dpy, config, share_context, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -285,6 +291,7 @@ Debug_eglCreateImageKHR(EGLDisplay dpy, << "(" << dpy << ", " << ctx << ", " << target << ", " << buffer << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglCreateImageKHRFn != nullptr); EGLImageKHR result = g_driver_egl.debug_fn.eglCreateImageKHRFn( dpy, ctx, target, buffer, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -301,6 +308,7 @@ Debug_eglCreatePbufferFromClientBuffer(EGLDisplay dpy, << "(" << dpy << ", " << buftype << ", " << static_cast<const void*>(buffer) << ", " << config << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglCreatePbufferFromClientBufferFn != nullptr); EGLSurface result = g_driver_egl.debug_fn.eglCreatePbufferFromClientBufferFn( dpy, buftype, buffer, config, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -314,6 +322,7 @@ Debug_eglCreatePbufferSurface(EGLDisplay dpy, GL_SERVICE_LOG("eglCreatePbufferSurface" << "(" << dpy << ", " << config << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglCreatePbufferSurfaceFn != nullptr); EGLSurface result = g_driver_egl.debug_fn.eglCreatePbufferSurfaceFn(dpy, config, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -328,6 +337,7 @@ Debug_eglCreatePixmapSurface(EGLDisplay dpy, GL_SERVICE_LOG("eglCreatePixmapSurface" << "(" << dpy << ", " << config << ", " << pixmap << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglCreatePixmapSurfaceFn != nullptr); EGLSurface result = g_driver_egl.debug_fn.eglCreatePixmapSurfaceFn( dpy, config, pixmap, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -341,6 +351,7 @@ Debug_eglCreateSyncKHR(EGLDisplay dpy, GL_SERVICE_LOG("eglCreateSyncKHR" << "(" << dpy << ", " << type << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglCreateSyncKHRFn != nullptr); EGLSyncKHR result = g_driver_egl.debug_fn.eglCreateSyncKHRFn(dpy, type, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -355,6 +366,7 @@ Debug_eglCreateWindowSurface(EGLDisplay dpy, GL_SERVICE_LOG("eglCreateWindowSurface" << "(" << dpy << ", " << config << ", " << win << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglCreateWindowSurfaceFn != nullptr); EGLSurface result = g_driver_egl.debug_fn.eglCreateWindowSurfaceFn( dpy, config, win, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -365,6 +377,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglDestroyContext(EGLDisplay dpy, EGLContext ctx) { GL_SERVICE_LOG("eglDestroyContext" << "(" << dpy << ", " << ctx << ")"); + DCHECK(g_driver_egl.debug_fn.eglDestroyContextFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglDestroyContextFn(dpy, ctx); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -374,6 +387,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image) { GL_SERVICE_LOG("eglDestroyImageKHR" << "(" << dpy << ", " << image << ")"); + DCHECK(g_driver_egl.debug_fn.eglDestroyImageKHRFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglDestroyImageKHRFn(dpy, image); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -383,6 +397,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { GL_SERVICE_LOG("eglDestroySurface" << "(" << dpy << ", " << surface << ")"); + DCHECK(g_driver_egl.debug_fn.eglDestroySurfaceFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglDestroySurfaceFn(dpy, surface); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -392,6 +407,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { GL_SERVICE_LOG("eglDestroySyncKHR" << "(" << dpy << ", " << sync << ")"); + DCHECK(g_driver_egl.debug_fn.eglDestroySyncKHRFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglDestroySyncKHRFn(dpy, sync); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -404,6 +420,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglGetConfigAttrib(EGLDisplay dpy, GL_SERVICE_LOG("eglGetConfigAttrib" << "(" << dpy << ", " << config << ", " << attribute << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetConfigAttribFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglGetConfigAttribFn(dpy, config, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -418,6 +435,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglGetConfigs(EGLDisplay dpy, << "(" << dpy << ", " << static_cast<const void*>(configs) << ", " << config_size << ", " << static_cast<const void*>(num_config) << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetConfigsFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglGetConfigsFn( dpy, configs, config_size, num_config); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -428,6 +446,7 @@ static EGLContext GL_BINDING_CALL Debug_eglGetCurrentContext(void) { GL_SERVICE_LOG("eglGetCurrentContext" << "(" << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetCurrentContextFn != nullptr); EGLContext result = g_driver_egl.debug_fn.eglGetCurrentContextFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -437,6 +456,7 @@ static EGLDisplay GL_BINDING_CALL Debug_eglGetCurrentDisplay(void) { GL_SERVICE_LOG("eglGetCurrentDisplay" << "(" << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetCurrentDisplayFn != nullptr); EGLDisplay result = g_driver_egl.debug_fn.eglGetCurrentDisplayFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -445,6 +465,7 @@ static EGLDisplay GL_BINDING_CALL Debug_eglGetCurrentDisplay(void) { static EGLSurface GL_BINDING_CALL Debug_eglGetCurrentSurface(EGLint readdraw) { GL_SERVICE_LOG("eglGetCurrentSurface" << "(" << readdraw << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetCurrentSurfaceFn != nullptr); EGLSurface result = g_driver_egl.debug_fn.eglGetCurrentSurfaceFn(readdraw); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -454,6 +475,7 @@ static EGLDisplay GL_BINDING_CALL Debug_eglGetDisplay(EGLNativeDisplayType display_id) { GL_SERVICE_LOG("eglGetDisplay" << "(" << display_id << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetDisplayFn != nullptr); EGLDisplay result = g_driver_egl.debug_fn.eglGetDisplayFn(display_id); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -463,6 +485,7 @@ static EGLint GL_BINDING_CALL Debug_eglGetError(void) { GL_SERVICE_LOG("eglGetError" << "(" << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetErrorFn != nullptr); EGLint result = g_driver_egl.debug_fn.eglGetErrorFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -476,6 +499,7 @@ Debug_eglGetPlatformDisplayEXT(EGLenum platform, << "(" << platform << ", " << static_cast<const void*>(native_display) << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetPlatformDisplayEXTFn != nullptr); EGLDisplay result = g_driver_egl.debug_fn.eglGetPlatformDisplayEXTFn( platform, native_display, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -486,6 +510,7 @@ static __eglMustCastToProperFunctionPointerType GL_BINDING_CALL Debug_eglGetProcAddress(const char* procname) { GL_SERVICE_LOG("eglGetProcAddress" << "(" << procname << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetProcAddressFn != nullptr); __eglMustCastToProperFunctionPointerType result = g_driver_egl.debug_fn.eglGetProcAddressFn(procname); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -499,6 +524,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglGetSyncAttribKHR(EGLDisplay dpy, GL_SERVICE_LOG("eglGetSyncAttribKHR" << "(" << dpy << ", " << sync << ", " << attribute << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetSyncAttribKHRFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglGetSyncAttribKHRFn(dpy, sync, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -516,6 +542,7 @@ Debug_eglGetSyncValuesCHROMIUM(EGLDisplay dpy, << static_cast<const void*>(ust) << ", " << static_cast<const void*>(msc) << ", " << static_cast<const void*>(sbc) << ")"); + DCHECK(g_driver_egl.debug_fn.eglGetSyncValuesCHROMIUMFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglGetSyncValuesCHROMIUMFn( dpy, surface, ust, msc, sbc); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -528,6 +555,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglInitialize(EGLDisplay dpy, GL_SERVICE_LOG("eglInitialize" << "(" << dpy << ", " << static_cast<const void*>(major) << ", " << static_cast<const void*>(minor) << ")"); + DCHECK(g_driver_egl.debug_fn.eglInitializeFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglInitializeFn(dpy, major, minor); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -540,6 +568,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglMakeCurrent(EGLDisplay dpy, GL_SERVICE_LOG("eglMakeCurrent" << "(" << dpy << ", " << draw << ", " << read << ", " << ctx << ")"); + DCHECK(g_driver_egl.debug_fn.eglMakeCurrentFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglMakeCurrentFn(dpy, draw, read, ctx); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -555,6 +584,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglPostSubBufferNV(EGLDisplay dpy, GL_SERVICE_LOG("eglPostSubBufferNV" << "(" << dpy << ", " << surface << ", " << x << ", " << y << ", " << width << ", " << height << ")"); + DCHECK(g_driver_egl.debug_fn.eglPostSubBufferNVFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglPostSubBufferNVFn( dpy, surface, x, y, width, height); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -565,6 +595,7 @@ static EGLenum GL_BINDING_CALL Debug_eglQueryAPI(void) { GL_SERVICE_LOG("eglQueryAPI" << "(" << ")"); + DCHECK(g_driver_egl.debug_fn.eglQueryAPIFn != nullptr); EGLenum result = g_driver_egl.debug_fn.eglQueryAPIFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -577,6 +608,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglQueryContext(EGLDisplay dpy, GL_SERVICE_LOG("eglQueryContext" << "(" << dpy << ", " << ctx << ", " << attribute << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_egl.debug_fn.eglQueryContextFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglQueryContextFn(dpy, ctx, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -587,6 +619,7 @@ static const char* GL_BINDING_CALL Debug_eglQueryString(EGLDisplay dpy, EGLint name) { GL_SERVICE_LOG("eglQueryString" << "(" << dpy << ", " << name << ")"); + DCHECK(g_driver_egl.debug_fn.eglQueryStringFn != nullptr); const char* result = g_driver_egl.debug_fn.eglQueryStringFn(dpy, name); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -599,6 +632,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglQuerySurface(EGLDisplay dpy, GL_SERVICE_LOG("eglQuerySurface" << "(" << dpy << ", " << surface << ", " << attribute << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_egl.debug_fn.eglQuerySurfaceFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglQuerySurfaceFn(dpy, surface, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -613,6 +647,7 @@ Debug_eglQuerySurfacePointerANGLE(EGLDisplay dpy, GL_SERVICE_LOG("eglQuerySurfacePointerANGLE" << "(" << dpy << ", " << surface << ", " << attribute << ", " << value << ")"); + DCHECK(g_driver_egl.debug_fn.eglQuerySurfacePointerANGLEFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglQuerySurfacePointerANGLEFn( dpy, surface, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -624,6 +659,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglReleaseTexImage(EGLDisplay dpy, EGLint buffer) { GL_SERVICE_LOG("eglReleaseTexImage" << "(" << dpy << ", " << surface << ", " << buffer << ")"); + DCHECK(g_driver_egl.debug_fn.eglReleaseTexImageFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglReleaseTexImageFn(dpy, surface, buffer); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -634,6 +670,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglReleaseThread(void) { GL_SERVICE_LOG("eglReleaseThread" << "(" << ")"); + DCHECK(g_driver_egl.debug_fn.eglReleaseThreadFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglReleaseThreadFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -646,6 +683,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglSurfaceAttrib(EGLDisplay dpy, GL_SERVICE_LOG("eglSurfaceAttrib" << "(" << dpy << ", " << surface << ", " << attribute << ", " << value << ")"); + DCHECK(g_driver_egl.debug_fn.eglSurfaceAttribFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglSurfaceAttribFn(dpy, surface, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -656,6 +694,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { GL_SERVICE_LOG("eglSwapBuffers" << "(" << dpy << ", " << surface << ")"); + DCHECK(g_driver_egl.debug_fn.eglSwapBuffersFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglSwapBuffersFn(dpy, surface); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -665,6 +704,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglSwapInterval(EGLDisplay dpy, EGLint interval) { GL_SERVICE_LOG("eglSwapInterval" << "(" << dpy << ", " << interval << ")"); + DCHECK(g_driver_egl.debug_fn.eglSwapIntervalFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglSwapIntervalFn(dpy, interval); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -673,6 +713,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglSwapInterval(EGLDisplay dpy, static EGLBoolean GL_BINDING_CALL Debug_eglTerminate(EGLDisplay dpy) { GL_SERVICE_LOG("eglTerminate" << "(" << dpy << ")"); + DCHECK(g_driver_egl.debug_fn.eglTerminateFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglTerminateFn(dpy); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -682,6 +723,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglWaitClient(void) { GL_SERVICE_LOG("eglWaitClient" << "(" << ")"); + DCHECK(g_driver_egl.debug_fn.eglWaitClientFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglWaitClientFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -691,6 +733,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglWaitGL(void) { GL_SERVICE_LOG("eglWaitGL" << "(" << ")"); + DCHECK(g_driver_egl.debug_fn.eglWaitGLFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglWaitGLFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -699,6 +742,7 @@ static EGLBoolean GL_BINDING_CALL Debug_eglWaitGL(void) { static EGLBoolean GL_BINDING_CALL Debug_eglWaitNative(EGLint engine) { GL_SERVICE_LOG("eglWaitNative" << "(" << engine << ")"); + DCHECK(g_driver_egl.debug_fn.eglWaitNativeFn != nullptr); EGLBoolean result = g_driver_egl.debug_fn.eglWaitNativeFn(engine); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -709,6 +753,7 @@ static EGLint GL_BINDING_CALL Debug_eglWaitSyncKHR(EGLDisplay dpy, EGLint flags) { GL_SERVICE_LOG("eglWaitSyncKHR" << "(" << dpy << ", " << sync << ", " << flags << ")"); + DCHECK(g_driver_egl.debug_fn.eglWaitSyncKHRFn != nullptr); EGLint result = g_driver_egl.debug_fn.eglWaitSyncKHRFn(dpy, sync, flags); GL_SERVICE_LOG("GL_RESULT: " << result); return result; diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.cc b/chromium/ui/gl/gl_bindings_autogen_gl.cc index 8cc565051af..c768ae75e35 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_gl.cc @@ -311,6 +311,7 @@ void DriverGL::InitializeStaticBindings() { fn.glPolygonOffsetFn = reinterpret_cast<glPolygonOffsetProc>( GetGLProcAddress("glPolygonOffset")); fn.glPopGroupMarkerEXTFn = 0; + fn.glPrimitiveRestartIndexFn = 0; fn.glProgramBinaryFn = 0; fn.glProgramParameteriFn = 0; fn.glProgramPathFragmentInputGenNVFn = 0; @@ -552,6 +553,8 @@ void DriverGL::InitializeDynamicBindings(GLContext* context) { extensions.find("GL_EXT_texture_storage ") != std::string::npos; ext.b_GL_EXT_timer_query = extensions.find("GL_EXT_timer_query ") != std::string::npos; + ext.b_GL_EXT_unpack_subimage = + extensions.find("GL_EXT_unpack_subimage ") != std::string::npos; ext.b_GL_IMG_multisampled_render_to_texture = extensions.find("GL_IMG_multisampled_render_to_texture ") != std::string::npos; @@ -1632,6 +1635,13 @@ void DriverGL::InitializeDynamicBindings(GLContext* context) { GetGLProcAddress("glPopGroupMarkerEXT")); } + debug_fn.glPrimitiveRestartIndexFn = 0; + if (ver->IsAtLeastGL(3u, 1u)) { + fn.glPrimitiveRestartIndexFn = + reinterpret_cast<glPrimitiveRestartIndexProc>( + GetGLProcAddress("glPrimitiveRestartIndex")); + } + debug_fn.glProgramBinaryFn = 0; if (ver->IsAtLeastGL(4u, 1u) || ver->IsAtLeastGLES(3u, 0u) || ext.b_GL_ARB_get_program_binary) { @@ -2034,6 +2044,7 @@ extern "C" { static void GL_BINDING_CALL Debug_glActiveTexture(GLenum texture) { GL_SERVICE_LOG("glActiveTexture" << "(" << GLEnums::GetStringEnum(texture) << ")"); + DCHECK(g_driver_gl.debug_fn.glActiveTextureFn != nullptr); g_driver_gl.debug_fn.glActiveTextureFn(texture); } @@ -2041,6 +2052,8 @@ static void GL_BINDING_CALL Debug_glApplyFramebufferAttachmentCMAAINTEL(void) { GL_SERVICE_LOG("glApplyFramebufferAttachmentCMAAINTEL" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glApplyFramebufferAttachmentCMAAINTELFn != + nullptr); g_driver_gl.debug_fn.glApplyFramebufferAttachmentCMAAINTELFn(); } @@ -2048,12 +2061,14 @@ static void GL_BINDING_CALL Debug_glAttachShader(GLuint program, GLuint shader) { GL_SERVICE_LOG("glAttachShader" << "(" << program << ", " << shader << ")"); + DCHECK(g_driver_gl.debug_fn.glAttachShaderFn != nullptr); g_driver_gl.debug_fn.glAttachShaderFn(program, shader); } static void GL_BINDING_CALL Debug_glBeginQuery(GLenum target, GLuint id) { GL_SERVICE_LOG("glBeginQuery" << "(" << GLEnums::GetStringEnum(target) << ", " << id << ")"); + DCHECK(g_driver_gl.debug_fn.glBeginQueryFn != nullptr); g_driver_gl.debug_fn.glBeginQueryFn(target, id); } @@ -2061,6 +2076,7 @@ static void GL_BINDING_CALL Debug_glBeginTransformFeedback(GLenum primitiveMode) { GL_SERVICE_LOG("glBeginTransformFeedback" << "(" << GLEnums::GetStringEnum(primitiveMode) << ")"); + DCHECK(g_driver_gl.debug_fn.glBeginTransformFeedbackFn != nullptr); g_driver_gl.debug_fn.glBeginTransformFeedbackFn(primitiveMode); } @@ -2069,6 +2085,7 @@ static void GL_BINDING_CALL Debug_glBindAttribLocation(GLuint program, const char* name) { GL_SERVICE_LOG("glBindAttribLocation" << "(" << program << ", " << index << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glBindAttribLocationFn != nullptr); g_driver_gl.debug_fn.glBindAttribLocationFn(program, index, name); } @@ -2076,6 +2093,7 @@ static void GL_BINDING_CALL Debug_glBindBuffer(GLenum target, GLuint buffer) { GL_SERVICE_LOG("glBindBuffer" << "(" << GLEnums::GetStringEnum(target) << ", " << buffer << ")"); + DCHECK(g_driver_gl.debug_fn.glBindBufferFn != nullptr); g_driver_gl.debug_fn.glBindBufferFn(target, buffer); } @@ -2085,6 +2103,7 @@ static void GL_BINDING_CALL Debug_glBindBufferBase(GLenum target, GL_SERVICE_LOG("glBindBufferBase" << "(" << GLEnums::GetStringEnum(target) << ", " << index << ", " << buffer << ")"); + DCHECK(g_driver_gl.debug_fn.glBindBufferBaseFn != nullptr); g_driver_gl.debug_fn.glBindBufferBaseFn(target, index, buffer); } @@ -2096,6 +2115,7 @@ static void GL_BINDING_CALL Debug_glBindBufferRange(GLenum target, GL_SERVICE_LOG("glBindBufferRange" << "(" << GLEnums::GetStringEnum(target) << ", " << index << ", " << buffer << ", " << offset << ", " << size << ")"); + DCHECK(g_driver_gl.debug_fn.glBindBufferRangeFn != nullptr); g_driver_gl.debug_fn.glBindBufferRangeFn(target, index, buffer, offset, size); } @@ -2105,6 +2125,7 @@ static void GL_BINDING_CALL Debug_glBindFragDataLocation(GLuint program, GL_SERVICE_LOG("glBindFragDataLocation" << "(" << program << ", " << colorNumber << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glBindFragDataLocationFn != nullptr); g_driver_gl.debug_fn.glBindFragDataLocationFn(program, colorNumber, name); } @@ -2116,6 +2137,7 @@ Debug_glBindFragDataLocationIndexed(GLuint program, GL_SERVICE_LOG("glBindFragDataLocationIndexed" << "(" << program << ", " << colorNumber << ", " << index << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glBindFragDataLocationIndexedFn != nullptr); g_driver_gl.debug_fn.glBindFragDataLocationIndexedFn(program, colorNumber, index, name); } @@ -2125,6 +2147,7 @@ static void GL_BINDING_CALL Debug_glBindFramebufferEXT(GLenum target, GL_SERVICE_LOG("glBindFramebufferEXT" << "(" << GLEnums::GetStringEnum(target) << ", " << framebuffer << ")"); + DCHECK(g_driver_gl.debug_fn.glBindFramebufferEXTFn != nullptr); g_driver_gl.debug_fn.glBindFramebufferEXTFn(target, framebuffer); } @@ -2139,6 +2162,7 @@ static void GL_BINDING_CALL Debug_glBindImageTextureEXT(GLuint index, << "(" << index << ", " << texture << ", " << level << ", " << GLEnums::GetStringBool(layered) << ", " << layer << ", " << GLEnums::GetStringEnum(access) << ", " << format << ")"); + DCHECK(g_driver_gl.debug_fn.glBindImageTextureEXTFn != nullptr); g_driver_gl.debug_fn.glBindImageTextureEXTFn(index, texture, level, layered, layer, access, format); } @@ -2148,12 +2172,14 @@ static void GL_BINDING_CALL Debug_glBindRenderbufferEXT(GLenum target, GL_SERVICE_LOG("glBindRenderbufferEXT" << "(" << GLEnums::GetStringEnum(target) << ", " << renderbuffer << ")"); + DCHECK(g_driver_gl.debug_fn.glBindRenderbufferEXTFn != nullptr); g_driver_gl.debug_fn.glBindRenderbufferEXTFn(target, renderbuffer); } static void GL_BINDING_CALL Debug_glBindSampler(GLuint unit, GLuint sampler) { GL_SERVICE_LOG("glBindSampler" << "(" << unit << ", " << sampler << ")"); + DCHECK(g_driver_gl.debug_fn.glBindSamplerFn != nullptr); g_driver_gl.debug_fn.glBindSamplerFn(unit, sampler); } @@ -2161,6 +2187,7 @@ static void GL_BINDING_CALL Debug_glBindTexture(GLenum target, GLuint texture) { GL_SERVICE_LOG("glBindTexture" << "(" << GLEnums::GetStringEnum(target) << ", " << texture << ")"); + DCHECK(g_driver_gl.debug_fn.glBindTextureFn != nullptr); g_driver_gl.debug_fn.glBindTextureFn(target, texture); } @@ -2168,12 +2195,14 @@ static void GL_BINDING_CALL Debug_glBindTransformFeedback(GLenum target, GLuint id) { GL_SERVICE_LOG("glBindTransformFeedback" << "(" << GLEnums::GetStringEnum(target) << ", " << id << ")"); + DCHECK(g_driver_gl.debug_fn.glBindTransformFeedbackFn != nullptr); g_driver_gl.debug_fn.glBindTransformFeedbackFn(target, id); } static void GL_BINDING_CALL Debug_glBindVertexArrayOES(GLuint array) { GL_SERVICE_LOG("glBindVertexArrayOES" << "(" << array << ")"); + DCHECK(g_driver_gl.debug_fn.glBindVertexArrayOESFn != nullptr); g_driver_gl.debug_fn.glBindVertexArrayOESFn(array); } @@ -2181,6 +2210,7 @@ static void GL_BINDING_CALL Debug_glBlendBarrierKHR(void) { GL_SERVICE_LOG("glBlendBarrierKHR" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glBlendBarrierKHRFn != nullptr); g_driver_gl.debug_fn.glBlendBarrierKHRFn(); } @@ -2191,12 +2221,14 @@ static void GL_BINDING_CALL Debug_glBlendColor(GLclampf red, GL_SERVICE_LOG("glBlendColor" << "(" << red << ", " << green << ", " << blue << ", " << alpha << ")"); + DCHECK(g_driver_gl.debug_fn.glBlendColorFn != nullptr); g_driver_gl.debug_fn.glBlendColorFn(red, green, blue, alpha); } static void GL_BINDING_CALL Debug_glBlendEquation(GLenum mode) { GL_SERVICE_LOG("glBlendEquation" << "(" << GLEnums::GetStringEnum(mode) << ")"); + DCHECK(g_driver_gl.debug_fn.glBlendEquationFn != nullptr); g_driver_gl.debug_fn.glBlendEquationFn(mode); } @@ -2205,6 +2237,7 @@ static void GL_BINDING_CALL Debug_glBlendEquationSeparate(GLenum modeRGB, GL_SERVICE_LOG("glBlendEquationSeparate" << "(" << GLEnums::GetStringEnum(modeRGB) << ", " << GLEnums::GetStringEnum(modeAlpha) << ")"); + DCHECK(g_driver_gl.debug_fn.glBlendEquationSeparateFn != nullptr); g_driver_gl.debug_fn.glBlendEquationSeparateFn(modeRGB, modeAlpha); } @@ -2212,6 +2245,7 @@ static void GL_BINDING_CALL Debug_glBlendFunc(GLenum sfactor, GLenum dfactor) { GL_SERVICE_LOG("glBlendFunc" << "(" << GLEnums::GetStringEnum(sfactor) << ", " << GLEnums::GetStringEnum(dfactor) << ")"); + DCHECK(g_driver_gl.debug_fn.glBlendFuncFn != nullptr); g_driver_gl.debug_fn.glBlendFuncFn(sfactor, dfactor); } @@ -2224,6 +2258,7 @@ static void GL_BINDING_CALL Debug_glBlendFuncSeparate(GLenum srcRGB, << GLEnums::GetStringEnum(dstRGB) << ", " << GLEnums::GetStringEnum(srcAlpha) << ", " << GLEnums::GetStringEnum(dstAlpha) << ")"); + DCHECK(g_driver_gl.debug_fn.glBlendFuncSeparateFn != nullptr); g_driver_gl.debug_fn.glBlendFuncSeparateFn(srcRGB, dstRGB, srcAlpha, dstAlpha); } @@ -2243,6 +2278,7 @@ static void GL_BINDING_CALL Debug_glBlitFramebuffer(GLint srcX0, << srcY1 << ", " << dstX0 << ", " << dstY0 << ", " << dstX1 << ", " << dstY1 << ", " << mask << ", " << GLEnums::GetStringEnum(filter) << ")"); + DCHECK(g_driver_gl.debug_fn.glBlitFramebufferFn != nullptr); g_driver_gl.debug_fn.glBlitFramebufferFn(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } @@ -2262,6 +2298,7 @@ static void GL_BINDING_CALL Debug_glBlitFramebufferANGLE(GLint srcX0, << srcY1 << ", " << dstX0 << ", " << dstY0 << ", " << dstX1 << ", " << dstY1 << ", " << mask << ", " << GLEnums::GetStringEnum(filter) << ")"); + DCHECK(g_driver_gl.debug_fn.glBlitFramebufferANGLEFn != nullptr); g_driver_gl.debug_fn.glBlitFramebufferANGLEFn( srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } @@ -2281,6 +2318,7 @@ static void GL_BINDING_CALL Debug_glBlitFramebufferEXT(GLint srcX0, << srcY1 << ", " << dstX0 << ", " << dstY0 << ", " << dstX1 << ", " << dstY1 << ", " << mask << ", " << GLEnums::GetStringEnum(filter) << ")"); + DCHECK(g_driver_gl.debug_fn.glBlitFramebufferEXTFn != nullptr); g_driver_gl.debug_fn.glBlitFramebufferEXTFn( srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } @@ -2293,6 +2331,7 @@ static void GL_BINDING_CALL Debug_glBufferData(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << size << ", " << static_cast<const void*>(data) << ", " << GLEnums::GetStringEnum(usage) << ")"); + DCHECK(g_driver_gl.debug_fn.glBufferDataFn != nullptr); g_driver_gl.debug_fn.glBufferDataFn(target, size, data, usage); } @@ -2304,12 +2343,14 @@ static void GL_BINDING_CALL Debug_glBufferSubData(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << offset << ", " << size << ", " << static_cast<const void*>(data) << ")"); + DCHECK(g_driver_gl.debug_fn.glBufferSubDataFn != nullptr); g_driver_gl.debug_fn.glBufferSubDataFn(target, offset, size, data); } static GLenum GL_BINDING_CALL Debug_glCheckFramebufferStatusEXT(GLenum target) { GL_SERVICE_LOG("glCheckFramebufferStatusEXT" << "(" << GLEnums::GetStringEnum(target) << ")"); + DCHECK(g_driver_gl.debug_fn.glCheckFramebufferStatusEXTFn != nullptr); GLenum result = g_driver_gl.debug_fn.glCheckFramebufferStatusEXTFn(target); GL_SERVICE_LOG("GL_RESULT: " << GLEnums::GetStringEnum(result)); @@ -2320,6 +2361,7 @@ static GLenum GL_BINDING_CALL Debug_glCheckFramebufferStatusEXT(GLenum target) { static void GL_BINDING_CALL Debug_glClear(GLbitfield mask) { GL_SERVICE_LOG("glClear" << "(" << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glClearFn != nullptr); g_driver_gl.debug_fn.glClearFn(mask); } @@ -2330,6 +2372,7 @@ static void GL_BINDING_CALL Debug_glClearBufferfi(GLenum buffer, GL_SERVICE_LOG("glClearBufferfi" << "(" << GLEnums::GetStringEnum(buffer) << ", " << drawbuffer << ", " << depth << ", " << stencil << ")"); + DCHECK(g_driver_gl.debug_fn.glClearBufferfiFn != nullptr); g_driver_gl.debug_fn.glClearBufferfiFn(buffer, drawbuffer, depth, stencil); } @@ -2339,6 +2382,7 @@ static void GL_BINDING_CALL Debug_glClearBufferfv(GLenum buffer, GL_SERVICE_LOG("glClearBufferfv" << "(" << GLEnums::GetStringEnum(buffer) << ", " << drawbuffer << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glClearBufferfvFn != nullptr); g_driver_gl.debug_fn.glClearBufferfvFn(buffer, drawbuffer, value); } @@ -2348,6 +2392,7 @@ static void GL_BINDING_CALL Debug_glClearBufferiv(GLenum buffer, GL_SERVICE_LOG("glClearBufferiv" << "(" << GLEnums::GetStringEnum(buffer) << ", " << drawbuffer << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glClearBufferivFn != nullptr); g_driver_gl.debug_fn.glClearBufferivFn(buffer, drawbuffer, value); } @@ -2357,6 +2402,7 @@ static void GL_BINDING_CALL Debug_glClearBufferuiv(GLenum buffer, GL_SERVICE_LOG("glClearBufferuiv" << "(" << GLEnums::GetStringEnum(buffer) << ", " << drawbuffer << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glClearBufferuivFn != nullptr); g_driver_gl.debug_fn.glClearBufferuivFn(buffer, drawbuffer, value); } @@ -2367,24 +2413,28 @@ static void GL_BINDING_CALL Debug_glClearColor(GLclampf red, GL_SERVICE_LOG("glClearColor" << "(" << red << ", " << green << ", " << blue << ", " << alpha << ")"); + DCHECK(g_driver_gl.debug_fn.glClearColorFn != nullptr); g_driver_gl.debug_fn.glClearColorFn(red, green, blue, alpha); } static void GL_BINDING_CALL Debug_glClearDepth(GLclampd depth) { GL_SERVICE_LOG("glClearDepth" << "(" << depth << ")"); + DCHECK(g_driver_gl.debug_fn.glClearDepthFn != nullptr); g_driver_gl.debug_fn.glClearDepthFn(depth); } static void GL_BINDING_CALL Debug_glClearDepthf(GLclampf depth) { GL_SERVICE_LOG("glClearDepthf" << "(" << depth << ")"); + DCHECK(g_driver_gl.debug_fn.glClearDepthfFn != nullptr); g_driver_gl.debug_fn.glClearDepthfFn(depth); } static void GL_BINDING_CALL Debug_glClearStencil(GLint s) { GL_SERVICE_LOG("glClearStencil" << "(" << s << ")"); + DCHECK(g_driver_gl.debug_fn.glClearStencilFn != nullptr); g_driver_gl.debug_fn.glClearStencilFn(s); } @@ -2393,6 +2443,7 @@ static GLenum GL_BINDING_CALL Debug_glClientWaitSync(GLsync sync, GLuint64 timeout) { GL_SERVICE_LOG("glClientWaitSync" << "(" << sync << ", " << flags << ", " << timeout << ")"); + DCHECK(g_driver_gl.debug_fn.glClientWaitSyncFn != nullptr); GLenum result = g_driver_gl.debug_fn.glClientWaitSyncFn(sync, flags, timeout); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -2407,12 +2458,14 @@ static void GL_BINDING_CALL Debug_glColorMask(GLboolean red, << GLEnums::GetStringBool(green) << ", " << GLEnums::GetStringBool(blue) << ", " << GLEnums::GetStringBool(alpha) << ")"); + DCHECK(g_driver_gl.debug_fn.glColorMaskFn != nullptr); g_driver_gl.debug_fn.glColorMaskFn(red, green, blue, alpha); } static void GL_BINDING_CALL Debug_glCompileShader(GLuint shader) { GL_SERVICE_LOG("glCompileShader" << "(" << shader << ")"); + DCHECK(g_driver_gl.debug_fn.glCompileShaderFn != nullptr); g_driver_gl.debug_fn.glCompileShaderFn(shader); } @@ -2429,6 +2482,7 @@ static void GL_BINDING_CALL Debug_glCompressedTexImage2D(GLenum target, << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ", " << border << ", " << imageSize << ", " << static_cast<const void*>(data) << ")"); + DCHECK(g_driver_gl.debug_fn.glCompressedTexImage2DFn != nullptr); g_driver_gl.debug_fn.glCompressedTexImage2DFn( target, level, internalformat, width, height, border, imageSize, data); } @@ -2448,6 +2502,7 @@ static void GL_BINDING_CALL Debug_glCompressedTexImage3D(GLenum target, << width << ", " << height << ", " << depth << ", " << border << ", " << imageSize << ", " << static_cast<const void*>(data) << ")"); + DCHECK(g_driver_gl.debug_fn.glCompressedTexImage3DFn != nullptr); g_driver_gl.debug_fn.glCompressedTexImage3DFn(target, level, internalformat, width, height, depth, border, imageSize, data); @@ -2467,6 +2522,7 @@ static void GL_BINDING_CALL Debug_glCompressedTexSubImage2D(GLenum target, << ", " << xoffset << ", " << yoffset << ", " << width << ", " << height << ", " << GLEnums::GetStringEnum(format) << ", " << imageSize << ", " << static_cast<const void*>(data) << ")"); + DCHECK(g_driver_gl.debug_fn.glCompressedTexSubImage2DFn != nullptr); g_driver_gl.debug_fn.glCompressedTexSubImage2DFn( target, level, xoffset, yoffset, width, height, format, imageSize, data); } @@ -2488,6 +2544,7 @@ static void GL_BINDING_CALL Debug_glCompressedTexSubImage3D(GLenum target, << ", " << width << ", " << height << ", " << depth << ", " << GLEnums::GetStringEnum(format) << ", " << imageSize << ", " << static_cast<const void*>(data) << ")"); + DCHECK(g_driver_gl.debug_fn.glCompressedTexSubImage3DFn != nullptr); g_driver_gl.debug_fn.glCompressedTexSubImage3DFn( target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); @@ -2502,6 +2559,7 @@ static void GL_BINDING_CALL Debug_glCopyBufferSubData(GLenum readTarget, << "(" << GLEnums::GetStringEnum(readTarget) << ", " << GLEnums::GetStringEnum(writeTarget) << ", " << readOffset << ", " << writeOffset << ", " << size << ")"); + DCHECK(g_driver_gl.debug_fn.glCopyBufferSubDataFn != nullptr); g_driver_gl.debug_fn.glCopyBufferSubDataFn(readTarget, writeTarget, readOffset, writeOffset, size); } @@ -2519,6 +2577,7 @@ static void GL_BINDING_CALL Debug_glCopyTexImage2D(GLenum target, << ", " << GLEnums::GetStringEnum(internalformat) << ", " << x << ", " << y << ", " << width << ", " << height << ", " << border << ")"); + DCHECK(g_driver_gl.debug_fn.glCopyTexImage2DFn != nullptr); g_driver_gl.debug_fn.glCopyTexImage2DFn(target, level, internalformat, x, y, width, height, border); } @@ -2535,6 +2594,7 @@ static void GL_BINDING_CALL Debug_glCopyTexSubImage2D(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << level << ", " << xoffset << ", " << yoffset << ", " << x << ", " << y << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glCopyTexSubImage2DFn != nullptr); g_driver_gl.debug_fn.glCopyTexSubImage2DFn(target, level, xoffset, yoffset, x, y, width, height); } @@ -2553,6 +2613,7 @@ static void GL_BINDING_CALL Debug_glCopyTexSubImage3D(GLenum target, << ", " << xoffset << ", " << yoffset << ", " << zoffset << ", " << x << ", " << y << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glCopyTexSubImage3DFn != nullptr); g_driver_gl.debug_fn.glCopyTexSubImage3DFn(target, level, xoffset, yoffset, zoffset, x, y, width, height); } @@ -2560,6 +2621,7 @@ static void GL_BINDING_CALL Debug_glCopyTexSubImage3D(GLenum target, static void GL_BINDING_CALL Debug_glCoverageModulationNV(GLenum components) { GL_SERVICE_LOG("glCoverageModulationNV" << "(" << GLEnums::GetStringEnum(components) << ")"); + DCHECK(g_driver_gl.debug_fn.glCoverageModulationNVFn != nullptr); g_driver_gl.debug_fn.glCoverageModulationNVFn(components); } @@ -2578,6 +2640,7 @@ Debug_glCoverFillPathInstancedNV(GLsizei numPaths, << GLEnums::GetStringEnum(coverMode) << ", " << GLEnums::GetStringEnum(transformType) << ", " << static_cast<const void*>(transformValues) << ")"); + DCHECK(g_driver_gl.debug_fn.glCoverFillPathInstancedNVFn != nullptr); g_driver_gl.debug_fn.glCoverFillPathInstancedNVFn( numPaths, pathNameType, paths, pathBase, coverMode, transformType, transformValues); @@ -2588,6 +2651,7 @@ static void GL_BINDING_CALL Debug_glCoverFillPathNV(GLuint path, GL_SERVICE_LOG("glCoverFillPathNV" << "(" << path << ", " << GLEnums::GetStringEnum(coverMode) << ")"); + DCHECK(g_driver_gl.debug_fn.glCoverFillPathNVFn != nullptr); g_driver_gl.debug_fn.glCoverFillPathNVFn(path, coverMode); } @@ -2606,6 +2670,7 @@ Debug_glCoverStrokePathInstancedNV(GLsizei numPaths, << GLEnums::GetStringEnum(coverMode) << ", " << GLEnums::GetStringEnum(transformType) << ", " << static_cast<const void*>(transformValues) << ")"); + DCHECK(g_driver_gl.debug_fn.glCoverStrokePathInstancedNVFn != nullptr); g_driver_gl.debug_fn.glCoverStrokePathInstancedNVFn( numPaths, pathNameType, paths, pathBase, coverMode, transformType, transformValues); @@ -2616,6 +2681,7 @@ static void GL_BINDING_CALL Debug_glCoverStrokePathNV(GLuint name, GL_SERVICE_LOG("glCoverStrokePathNV" << "(" << name << ", " << GLEnums::GetStringEnum(coverMode) << ")"); + DCHECK(g_driver_gl.debug_fn.glCoverStrokePathNVFn != nullptr); g_driver_gl.debug_fn.glCoverStrokePathNVFn(name, coverMode); } @@ -2623,6 +2689,7 @@ static GLuint GL_BINDING_CALL Debug_glCreateProgram(void) { GL_SERVICE_LOG("glCreateProgram" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glCreateProgramFn != nullptr); GLuint result = g_driver_gl.debug_fn.glCreateProgramFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -2631,6 +2698,7 @@ static GLuint GL_BINDING_CALL Debug_glCreateProgram(void) { static GLuint GL_BINDING_CALL Debug_glCreateShader(GLenum type) { GL_SERVICE_LOG("glCreateShader" << "(" << GLEnums::GetStringEnum(type) << ")"); + DCHECK(g_driver_gl.debug_fn.glCreateShaderFn != nullptr); GLuint result = g_driver_gl.debug_fn.glCreateShaderFn(type); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -2639,6 +2707,7 @@ static GLuint GL_BINDING_CALL Debug_glCreateShader(GLenum type) { static void GL_BINDING_CALL Debug_glCullFace(GLenum mode) { GL_SERVICE_LOG("glCullFace" << "(" << GLEnums::GetStringEnum(mode) << ")"); + DCHECK(g_driver_gl.debug_fn.glCullFaceFn != nullptr); g_driver_gl.debug_fn.glCullFaceFn(mode); } @@ -2647,6 +2716,7 @@ static void GL_BINDING_CALL Debug_glDeleteBuffersARB(GLsizei n, GL_SERVICE_LOG("glDeleteBuffersARB" << "(" << n << ", " << static_cast<const void*>(buffers) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteBuffersARBFn != nullptr); g_driver_gl.debug_fn.glDeleteBuffersARBFn(n, buffers); } @@ -2655,6 +2725,7 @@ static void GL_BINDING_CALL Debug_glDeleteFencesAPPLE(GLsizei n, GL_SERVICE_LOG("glDeleteFencesAPPLE" << "(" << n << ", " << static_cast<const void*>(fences) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteFencesAPPLEFn != nullptr); g_driver_gl.debug_fn.glDeleteFencesAPPLEFn(n, fences); } @@ -2663,6 +2734,7 @@ static void GL_BINDING_CALL Debug_glDeleteFencesNV(GLsizei n, GL_SERVICE_LOG("glDeleteFencesNV" << "(" << n << ", " << static_cast<const void*>(fences) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteFencesNVFn != nullptr); g_driver_gl.debug_fn.glDeleteFencesNVFn(n, fences); } @@ -2671,18 +2743,21 @@ Debug_glDeleteFramebuffersEXT(GLsizei n, const GLuint* framebuffers) { GL_SERVICE_LOG("glDeleteFramebuffersEXT" << "(" << n << ", " << static_cast<const void*>(framebuffers) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteFramebuffersEXTFn != nullptr); g_driver_gl.debug_fn.glDeleteFramebuffersEXTFn(n, framebuffers); } static void GL_BINDING_CALL Debug_glDeletePathsNV(GLuint path, GLsizei range) { GL_SERVICE_LOG("glDeletePathsNV" << "(" << path << ", " << range << ")"); + DCHECK(g_driver_gl.debug_fn.glDeletePathsNVFn != nullptr); g_driver_gl.debug_fn.glDeletePathsNVFn(path, range); } static void GL_BINDING_CALL Debug_glDeleteProgram(GLuint program) { GL_SERVICE_LOG("glDeleteProgram" << "(" << program << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteProgramFn != nullptr); g_driver_gl.debug_fn.glDeleteProgramFn(program); } @@ -2690,6 +2765,7 @@ static void GL_BINDING_CALL Debug_glDeleteQueries(GLsizei n, const GLuint* ids) { GL_SERVICE_LOG("glDeleteQueries" << "(" << n << ", " << static_cast<const void*>(ids) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteQueriesFn != nullptr); g_driver_gl.debug_fn.glDeleteQueriesFn(n, ids); } @@ -2698,6 +2774,7 @@ Debug_glDeleteRenderbuffersEXT(GLsizei n, const GLuint* renderbuffers) { GL_SERVICE_LOG("glDeleteRenderbuffersEXT" << "(" << n << ", " << static_cast<const void*>(renderbuffers) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteRenderbuffersEXTFn != nullptr); g_driver_gl.debug_fn.glDeleteRenderbuffersEXTFn(n, renderbuffers); } @@ -2706,18 +2783,21 @@ static void GL_BINDING_CALL Debug_glDeleteSamplers(GLsizei n, GL_SERVICE_LOG("glDeleteSamplers" << "(" << n << ", " << static_cast<const void*>(samplers) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteSamplersFn != nullptr); g_driver_gl.debug_fn.glDeleteSamplersFn(n, samplers); } static void GL_BINDING_CALL Debug_glDeleteShader(GLuint shader) { GL_SERVICE_LOG("glDeleteShader" << "(" << shader << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteShaderFn != nullptr); g_driver_gl.debug_fn.glDeleteShaderFn(shader); } static void GL_BINDING_CALL Debug_glDeleteSync(GLsync sync) { GL_SERVICE_LOG("glDeleteSync" << "(" << sync << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteSyncFn != nullptr); g_driver_gl.debug_fn.glDeleteSyncFn(sync); } @@ -2726,6 +2806,7 @@ static void GL_BINDING_CALL Debug_glDeleteTextures(GLsizei n, GL_SERVICE_LOG("glDeleteTextures" << "(" << n << ", " << static_cast<const void*>(textures) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteTexturesFn != nullptr); g_driver_gl.debug_fn.glDeleteTexturesFn(n, textures); } @@ -2733,6 +2814,7 @@ static void GL_BINDING_CALL Debug_glDeleteTransformFeedbacks(GLsizei n, const GLuint* ids) { GL_SERVICE_LOG("glDeleteTransformFeedbacks" << "(" << n << ", " << static_cast<const void*>(ids) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteTransformFeedbacksFn != nullptr); g_driver_gl.debug_fn.glDeleteTransformFeedbacksFn(n, ids); } @@ -2741,30 +2823,35 @@ Debug_glDeleteVertexArraysOES(GLsizei n, const GLuint* arrays) { GL_SERVICE_LOG("glDeleteVertexArraysOES" << "(" << n << ", " << static_cast<const void*>(arrays) << ")"); + DCHECK(g_driver_gl.debug_fn.glDeleteVertexArraysOESFn != nullptr); g_driver_gl.debug_fn.glDeleteVertexArraysOESFn(n, arrays); } static void GL_BINDING_CALL Debug_glDepthFunc(GLenum func) { GL_SERVICE_LOG("glDepthFunc" << "(" << GLEnums::GetStringEnum(func) << ")"); + DCHECK(g_driver_gl.debug_fn.glDepthFuncFn != nullptr); g_driver_gl.debug_fn.glDepthFuncFn(func); } static void GL_BINDING_CALL Debug_glDepthMask(GLboolean flag) { GL_SERVICE_LOG("glDepthMask" << "(" << GLEnums::GetStringBool(flag) << ")"); + DCHECK(g_driver_gl.debug_fn.glDepthMaskFn != nullptr); g_driver_gl.debug_fn.glDepthMaskFn(flag); } static void GL_BINDING_CALL Debug_glDepthRange(GLclampd zNear, GLclampd zFar) { GL_SERVICE_LOG("glDepthRange" << "(" << zNear << ", " << zFar << ")"); + DCHECK(g_driver_gl.debug_fn.glDepthRangeFn != nullptr); g_driver_gl.debug_fn.glDepthRangeFn(zNear, zFar); } static void GL_BINDING_CALL Debug_glDepthRangef(GLclampf zNear, GLclampf zFar) { GL_SERVICE_LOG("glDepthRangef" << "(" << zNear << ", " << zFar << ")"); + DCHECK(g_driver_gl.debug_fn.glDepthRangefFn != nullptr); g_driver_gl.debug_fn.glDepthRangefFn(zNear, zFar); } @@ -2772,18 +2859,21 @@ static void GL_BINDING_CALL Debug_glDetachShader(GLuint program, GLuint shader) { GL_SERVICE_LOG("glDetachShader" << "(" << program << ", " << shader << ")"); + DCHECK(g_driver_gl.debug_fn.glDetachShaderFn != nullptr); g_driver_gl.debug_fn.glDetachShaderFn(program, shader); } static void GL_BINDING_CALL Debug_glDisable(GLenum cap) { GL_SERVICE_LOG("glDisable" << "(" << GLEnums::GetStringEnum(cap) << ")"); + DCHECK(g_driver_gl.debug_fn.glDisableFn != nullptr); g_driver_gl.debug_fn.glDisableFn(cap); } static void GL_BINDING_CALL Debug_glDisableVertexAttribArray(GLuint index) { GL_SERVICE_LOG("glDisableVertexAttribArray" << "(" << index << ")"); + DCHECK(g_driver_gl.debug_fn.glDisableVertexAttribArrayFn != nullptr); g_driver_gl.debug_fn.glDisableVertexAttribArrayFn(index); } @@ -2795,6 +2885,7 @@ Debug_glDiscardFramebufferEXT(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << numAttachments << ", " << static_cast<const void*>(attachments) << ")"); + DCHECK(g_driver_gl.debug_fn.glDiscardFramebufferEXTFn != nullptr); g_driver_gl.debug_fn.glDiscardFramebufferEXTFn(target, numAttachments, attachments); } @@ -2805,6 +2896,7 @@ static void GL_BINDING_CALL Debug_glDrawArrays(GLenum mode, GL_SERVICE_LOG("glDrawArrays" << "(" << GLEnums::GetStringEnum(mode) << ", " << first << ", " << count << ")"); + DCHECK(g_driver_gl.debug_fn.glDrawArraysFn != nullptr); g_driver_gl.debug_fn.glDrawArraysFn(mode, first, count); } @@ -2816,6 +2908,7 @@ Debug_glDrawArraysInstancedANGLE(GLenum mode, GL_SERVICE_LOG("glDrawArraysInstancedANGLE" << "(" << GLEnums::GetStringEnum(mode) << ", " << first << ", " << count << ", " << primcount << ")"); + DCHECK(g_driver_gl.debug_fn.glDrawArraysInstancedANGLEFn != nullptr); g_driver_gl.debug_fn.glDrawArraysInstancedANGLEFn(mode, first, count, primcount); } @@ -2823,6 +2916,7 @@ Debug_glDrawArraysInstancedANGLE(GLenum mode, static void GL_BINDING_CALL Debug_glDrawBuffer(GLenum mode) { GL_SERVICE_LOG("glDrawBuffer" << "(" << GLEnums::GetStringEnum(mode) << ")"); + DCHECK(g_driver_gl.debug_fn.glDrawBufferFn != nullptr); g_driver_gl.debug_fn.glDrawBufferFn(mode); } @@ -2830,6 +2924,7 @@ static void GL_BINDING_CALL Debug_glDrawBuffersARB(GLsizei n, const GLenum* bufs) { GL_SERVICE_LOG("glDrawBuffersARB" << "(" << n << ", " << static_cast<const void*>(bufs) << ")"); + DCHECK(g_driver_gl.debug_fn.glDrawBuffersARBFn != nullptr); g_driver_gl.debug_fn.glDrawBuffersARBFn(n, bufs); } @@ -2841,6 +2936,7 @@ static void GL_BINDING_CALL Debug_glDrawElements(GLenum mode, << "(" << GLEnums::GetStringEnum(mode) << ", " << count << ", " << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(indices) << ")"); + DCHECK(g_driver_gl.debug_fn.glDrawElementsFn != nullptr); g_driver_gl.debug_fn.glDrawElementsFn(mode, count, type, indices); } @@ -2855,6 +2951,7 @@ Debug_glDrawElementsInstancedANGLE(GLenum mode, << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(indices) << ", " << primcount << ")"); + DCHECK(g_driver_gl.debug_fn.glDrawElementsInstancedANGLEFn != nullptr); g_driver_gl.debug_fn.glDrawElementsInstancedANGLEFn(mode, count, type, indices, primcount); } @@ -2869,6 +2966,7 @@ static void GL_BINDING_CALL Debug_glDrawRangeElements(GLenum mode, << "(" << GLEnums::GetStringEnum(mode) << ", " << start << ", " << end << ", " << count << ", " << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(indices) << ")"); + DCHECK(g_driver_gl.debug_fn.glDrawRangeElementsFn != nullptr); g_driver_gl.debug_fn.glDrawRangeElementsFn(mode, start, end, count, type, indices); } @@ -2879,6 +2977,8 @@ Debug_glEGLImageTargetRenderbufferStorageOES(GLenum target, GL_SERVICE_LOG("glEGLImageTargetRenderbufferStorageOES" << "(" << GLEnums::GetStringEnum(target) << ", " << image << ")"); + DCHECK(g_driver_gl.debug_fn.glEGLImageTargetRenderbufferStorageOESFn != + nullptr); g_driver_gl.debug_fn.glEGLImageTargetRenderbufferStorageOESFn(target, image); } @@ -2887,24 +2987,28 @@ Debug_glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) { GL_SERVICE_LOG("glEGLImageTargetTexture2DOES" << "(" << GLEnums::GetStringEnum(target) << ", " << image << ")"); + DCHECK(g_driver_gl.debug_fn.glEGLImageTargetTexture2DOESFn != nullptr); g_driver_gl.debug_fn.glEGLImageTargetTexture2DOESFn(target, image); } static void GL_BINDING_CALL Debug_glEnable(GLenum cap) { GL_SERVICE_LOG("glEnable" << "(" << GLEnums::GetStringEnum(cap) << ")"); + DCHECK(g_driver_gl.debug_fn.glEnableFn != nullptr); g_driver_gl.debug_fn.glEnableFn(cap); } static void GL_BINDING_CALL Debug_glEnableVertexAttribArray(GLuint index) { GL_SERVICE_LOG("glEnableVertexAttribArray" << "(" << index << ")"); + DCHECK(g_driver_gl.debug_fn.glEnableVertexAttribArrayFn != nullptr); g_driver_gl.debug_fn.glEnableVertexAttribArrayFn(index); } static void GL_BINDING_CALL Debug_glEndQuery(GLenum target) { GL_SERVICE_LOG("glEndQuery" << "(" << GLEnums::GetStringEnum(target) << ")"); + DCHECK(g_driver_gl.debug_fn.glEndQueryFn != nullptr); g_driver_gl.debug_fn.glEndQueryFn(target); } @@ -2912,6 +3016,7 @@ static void GL_BINDING_CALL Debug_glEndTransformFeedback(void) { GL_SERVICE_LOG("glEndTransformFeedback" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glEndTransformFeedbackFn != nullptr); g_driver_gl.debug_fn.glEndTransformFeedbackFn(); } @@ -2920,6 +3025,7 @@ static GLsync GL_BINDING_CALL Debug_glFenceSync(GLenum condition, GL_SERVICE_LOG("glFenceSync" << "(" << GLEnums::GetStringEnum(condition) << ", " << flags << ")"); + DCHECK(g_driver_gl.debug_fn.glFenceSyncFn != nullptr); GLsync result = g_driver_gl.debug_fn.glFenceSyncFn(condition, flags); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -2929,18 +3035,21 @@ static void GL_BINDING_CALL Debug_glFinish(void) { GL_SERVICE_LOG("glFinish" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glFinishFn != nullptr); g_driver_gl.debug_fn.glFinishFn(); } static void GL_BINDING_CALL Debug_glFinishFenceAPPLE(GLuint fence) { GL_SERVICE_LOG("glFinishFenceAPPLE" << "(" << fence << ")"); + DCHECK(g_driver_gl.debug_fn.glFinishFenceAPPLEFn != nullptr); g_driver_gl.debug_fn.glFinishFenceAPPLEFn(fence); } static void GL_BINDING_CALL Debug_glFinishFenceNV(GLuint fence) { GL_SERVICE_LOG("glFinishFenceNV" << "(" << fence << ")"); + DCHECK(g_driver_gl.debug_fn.glFinishFenceNVFn != nullptr); g_driver_gl.debug_fn.glFinishFenceNVFn(fence); } @@ -2948,6 +3057,7 @@ static void GL_BINDING_CALL Debug_glFlush(void) { GL_SERVICE_LOG("glFlush" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glFlushFn != nullptr); g_driver_gl.debug_fn.glFlushFn(); } @@ -2957,6 +3067,7 @@ static void GL_BINDING_CALL Debug_glFlushMappedBufferRange(GLenum target, GL_SERVICE_LOG("glFlushMappedBufferRange" << "(" << GLEnums::GetStringEnum(target) << ", " << offset << ", " << length << ")"); + DCHECK(g_driver_gl.debug_fn.glFlushMappedBufferRangeFn != nullptr); g_driver_gl.debug_fn.glFlushMappedBufferRangeFn(target, offset, length); } @@ -2970,6 +3081,7 @@ Debug_glFramebufferRenderbufferEXT(GLenum target, << GLEnums::GetStringEnum(attachment) << ", " << GLEnums::GetStringEnum(renderbuffertarget) << ", " << renderbuffer << ")"); + DCHECK(g_driver_gl.debug_fn.glFramebufferRenderbufferEXTFn != nullptr); g_driver_gl.debug_fn.glFramebufferRenderbufferEXTFn( target, attachment, renderbuffertarget, renderbuffer); } @@ -2984,6 +3096,7 @@ static void GL_BINDING_CALL Debug_glFramebufferTexture2DEXT(GLenum target, << GLEnums::GetStringEnum(attachment) << ", " << GLEnums::GetStringEnum(textarget) << ", " << texture << ", " << level << ")"); + DCHECK(g_driver_gl.debug_fn.glFramebufferTexture2DEXTFn != nullptr); g_driver_gl.debug_fn.glFramebufferTexture2DEXTFn(target, attachment, textarget, texture, level); } @@ -3000,6 +3113,8 @@ Debug_glFramebufferTexture2DMultisampleEXT(GLenum target, << GLEnums::GetStringEnum(attachment) << ", " << GLEnums::GetStringEnum(textarget) << ", " << texture << ", " << level << ", " << samples << ")"); + DCHECK(g_driver_gl.debug_fn.glFramebufferTexture2DMultisampleEXTFn != + nullptr); g_driver_gl.debug_fn.glFramebufferTexture2DMultisampleEXTFn( target, attachment, textarget, texture, level, samples); } @@ -3016,6 +3131,8 @@ Debug_glFramebufferTexture2DMultisampleIMG(GLenum target, << GLEnums::GetStringEnum(attachment) << ", " << GLEnums::GetStringEnum(textarget) << ", " << texture << ", " << level << ", " << samples << ")"); + DCHECK(g_driver_gl.debug_fn.glFramebufferTexture2DMultisampleIMGFn != + nullptr); g_driver_gl.debug_fn.glFramebufferTexture2DMultisampleIMGFn( target, attachment, textarget, texture, level, samples); } @@ -3029,6 +3146,7 @@ static void GL_BINDING_CALL Debug_glFramebufferTextureLayer(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(attachment) << ", " << texture << ", " << level << ", " << layer << ")"); + DCHECK(g_driver_gl.debug_fn.glFramebufferTextureLayerFn != nullptr); g_driver_gl.debug_fn.glFramebufferTextureLayerFn(target, attachment, texture, level, layer); } @@ -3036,6 +3154,7 @@ static void GL_BINDING_CALL Debug_glFramebufferTextureLayer(GLenum target, static void GL_BINDING_CALL Debug_glFrontFace(GLenum mode) { GL_SERVICE_LOG("glFrontFace" << "(" << GLEnums::GetStringEnum(mode) << ")"); + DCHECK(g_driver_gl.debug_fn.glFrontFaceFn != nullptr); g_driver_gl.debug_fn.glFrontFaceFn(mode); } @@ -3043,12 +3162,14 @@ static void GL_BINDING_CALL Debug_glGenBuffersARB(GLsizei n, GLuint* buffers) { GL_SERVICE_LOG("glGenBuffersARB" << "(" << n << ", " << static_cast<const void*>(buffers) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenBuffersARBFn != nullptr); g_driver_gl.debug_fn.glGenBuffersARBFn(n, buffers); } static void GL_BINDING_CALL Debug_glGenerateMipmapEXT(GLenum target) { GL_SERVICE_LOG("glGenerateMipmapEXT" << "(" << GLEnums::GetStringEnum(target) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenerateMipmapEXTFn != nullptr); g_driver_gl.debug_fn.glGenerateMipmapEXTFn(target); } @@ -3056,6 +3177,7 @@ static void GL_BINDING_CALL Debug_glGenFencesAPPLE(GLsizei n, GLuint* fences) { GL_SERVICE_LOG("glGenFencesAPPLE" << "(" << n << ", " << static_cast<const void*>(fences) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenFencesAPPLEFn != nullptr); g_driver_gl.debug_fn.glGenFencesAPPLEFn(n, fences); } @@ -3063,6 +3185,7 @@ static void GL_BINDING_CALL Debug_glGenFencesNV(GLsizei n, GLuint* fences) { GL_SERVICE_LOG("glGenFencesNV" << "(" << n << ", " << static_cast<const void*>(fences) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenFencesNVFn != nullptr); g_driver_gl.debug_fn.glGenFencesNVFn(n, fences); } @@ -3071,12 +3194,14 @@ static void GL_BINDING_CALL Debug_glGenFramebuffersEXT(GLsizei n, GL_SERVICE_LOG("glGenFramebuffersEXT" << "(" << n << ", " << static_cast<const void*>(framebuffers) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenFramebuffersEXTFn != nullptr); g_driver_gl.debug_fn.glGenFramebuffersEXTFn(n, framebuffers); } static GLuint GL_BINDING_CALL Debug_glGenPathsNV(GLsizei range) { GL_SERVICE_LOG("glGenPathsNV" << "(" << range << ")"); + DCHECK(g_driver_gl.debug_fn.glGenPathsNVFn != nullptr); GLuint result = g_driver_gl.debug_fn.glGenPathsNVFn(range); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3085,6 +3210,7 @@ static GLuint GL_BINDING_CALL Debug_glGenPathsNV(GLsizei range) { static void GL_BINDING_CALL Debug_glGenQueries(GLsizei n, GLuint* ids) { GL_SERVICE_LOG("glGenQueries" << "(" << n << ", " << static_cast<const void*>(ids) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenQueriesFn != nullptr); g_driver_gl.debug_fn.glGenQueriesFn(n, ids); } @@ -3093,6 +3219,7 @@ static void GL_BINDING_CALL Debug_glGenRenderbuffersEXT(GLsizei n, GL_SERVICE_LOG("glGenRenderbuffersEXT" << "(" << n << ", " << static_cast<const void*>(renderbuffers) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenRenderbuffersEXTFn != nullptr); g_driver_gl.debug_fn.glGenRenderbuffersEXTFn(n, renderbuffers); } @@ -3100,6 +3227,7 @@ static void GL_BINDING_CALL Debug_glGenSamplers(GLsizei n, GLuint* samplers) { GL_SERVICE_LOG("glGenSamplers" << "(" << n << ", " << static_cast<const void*>(samplers) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenSamplersFn != nullptr); g_driver_gl.debug_fn.glGenSamplersFn(n, samplers); } @@ -3107,6 +3235,7 @@ static void GL_BINDING_CALL Debug_glGenTextures(GLsizei n, GLuint* textures) { GL_SERVICE_LOG("glGenTextures" << "(" << n << ", " << static_cast<const void*>(textures) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenTexturesFn != nullptr); g_driver_gl.debug_fn.glGenTexturesFn(n, textures); } @@ -3114,6 +3243,7 @@ static void GL_BINDING_CALL Debug_glGenTransformFeedbacks(GLsizei n, GLuint* ids) { GL_SERVICE_LOG("glGenTransformFeedbacks" << "(" << n << ", " << static_cast<const void*>(ids) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenTransformFeedbacksFn != nullptr); g_driver_gl.debug_fn.glGenTransformFeedbacksFn(n, ids); } @@ -3122,6 +3252,7 @@ static void GL_BINDING_CALL Debug_glGenVertexArraysOES(GLsizei n, GL_SERVICE_LOG("glGenVertexArraysOES" << "(" << n << ", " << static_cast<const void*>(arrays) << ")"); + DCHECK(g_driver_gl.debug_fn.glGenVertexArraysOESFn != nullptr); g_driver_gl.debug_fn.glGenVertexArraysOESFn(n, arrays); } @@ -3138,6 +3269,7 @@ static void GL_BINDING_CALL Debug_glGetActiveAttrib(GLuint program, << static_cast<const void*>(size) << ", " << static_cast<const void*>(type) << ", " << static_cast<const void*>(name) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetActiveAttribFn != nullptr); g_driver_gl.debug_fn.glGetActiveAttribFn(program, index, bufsize, length, size, type, name); } @@ -3155,6 +3287,7 @@ static void GL_BINDING_CALL Debug_glGetActiveUniform(GLuint program, << static_cast<const void*>(size) << ", " << static_cast<const void*>(type) << ", " << static_cast<const void*>(name) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetActiveUniformFn != nullptr); g_driver_gl.debug_fn.glGetActiveUniformFn(program, index, bufsize, length, size, type, name); } @@ -3168,6 +3301,7 @@ Debug_glGetActiveUniformBlockiv(GLuint program, << "(" << program << ", " << uniformBlockIndex << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetActiveUniformBlockivFn != nullptr); g_driver_gl.debug_fn.glGetActiveUniformBlockivFn(program, uniformBlockIndex, pname, params); } @@ -3182,6 +3316,7 @@ Debug_glGetActiveUniformBlockName(GLuint program, << "(" << program << ", " << uniformBlockIndex << ", " << bufSize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(uniformBlockName) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetActiveUniformBlockNameFn != nullptr); g_driver_gl.debug_fn.glGetActiveUniformBlockNameFn( program, uniformBlockIndex, bufSize, length, uniformBlockName); } @@ -3197,6 +3332,7 @@ Debug_glGetActiveUniformsiv(GLuint program, << static_cast<const void*>(uniformIndices) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetActiveUniformsivFn != nullptr); g_driver_gl.debug_fn.glGetActiveUniformsivFn(program, uniformCount, uniformIndices, pname, params); } @@ -3209,6 +3345,7 @@ static void GL_BINDING_CALL Debug_glGetAttachedShaders(GLuint program, << "(" << program << ", " << maxcount << ", " << static_cast<const void*>(count) << ", " << static_cast<const void*>(shaders) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetAttachedShadersFn != nullptr); g_driver_gl.debug_fn.glGetAttachedShadersFn(program, maxcount, count, shaders); } @@ -3217,6 +3354,7 @@ static GLint GL_BINDING_CALL Debug_glGetAttribLocation(GLuint program, const char* name) { GL_SERVICE_LOG("glGetAttribLocation" << "(" << program << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glGetAttribLocationFn != nullptr); GLint result = g_driver_gl.debug_fn.glGetAttribLocationFn(program, name); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3227,6 +3365,7 @@ static void GL_BINDING_CALL Debug_glGetBooleanv(GLenum pname, GL_SERVICE_LOG("glGetBooleanv" << "(" << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetBooleanvFn != nullptr); g_driver_gl.debug_fn.glGetBooleanvFn(pname, params); } @@ -3237,6 +3376,7 @@ static void GL_BINDING_CALL Debug_glGetBufferParameteriv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetBufferParameterivFn != nullptr); g_driver_gl.debug_fn.glGetBufferParameterivFn(target, pname, params); } @@ -3244,6 +3384,7 @@ static GLenum GL_BINDING_CALL Debug_glGetError(void) { GL_SERVICE_LOG("glGetError" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glGetErrorFn != nullptr); GLenum result = g_driver_gl.debug_fn.glGetErrorFn(); GL_SERVICE_LOG("GL_RESULT: " << GLEnums::GetStringError(result)); @@ -3257,6 +3398,7 @@ static void GL_BINDING_CALL Debug_glGetFenceivNV(GLuint fence, GL_SERVICE_LOG("glGetFenceivNV" << "(" << fence << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetFenceivNVFn != nullptr); g_driver_gl.debug_fn.glGetFenceivNVFn(fence, pname, params); } @@ -3264,6 +3406,7 @@ static void GL_BINDING_CALL Debug_glGetFloatv(GLenum pname, GLfloat* params) { GL_SERVICE_LOG("glGetFloatv" << "(" << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetFloatvFn != nullptr); g_driver_gl.debug_fn.glGetFloatvFn(pname, params); } @@ -3271,6 +3414,7 @@ static GLint GL_BINDING_CALL Debug_glGetFragDataIndex(GLuint program, const char* name) { GL_SERVICE_LOG("glGetFragDataIndex" << "(" << program << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glGetFragDataIndexFn != nullptr); GLint result = g_driver_gl.debug_fn.glGetFragDataIndexFn(program, name); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3280,6 +3424,7 @@ static GLint GL_BINDING_CALL Debug_glGetFragDataLocation(GLuint program, const char* name) { GL_SERVICE_LOG("glGetFragDataLocation" << "(" << program << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glGetFragDataLocationFn != nullptr); GLint result = g_driver_gl.debug_fn.glGetFragDataLocationFn(program, name); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3295,6 +3440,8 @@ Debug_glGetFramebufferAttachmentParameterivEXT(GLenum target, << GLEnums::GetStringEnum(attachment) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetFramebufferAttachmentParameterivEXTFn != + nullptr); g_driver_gl.debug_fn.glGetFramebufferAttachmentParameterivEXTFn( target, attachment, pname, params); } @@ -3303,6 +3450,7 @@ static GLenum GL_BINDING_CALL Debug_glGetGraphicsResetStatusARB(void) { GL_SERVICE_LOG("glGetGraphicsResetStatusARB" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glGetGraphicsResetStatusARBFn != nullptr); GLenum result = g_driver_gl.debug_fn.glGetGraphicsResetStatusARBFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3314,6 +3462,7 @@ static void GL_BINDING_CALL Debug_glGetInteger64i_v(GLenum target, GL_SERVICE_LOG("glGetInteger64i_v" << "(" << GLEnums::GetStringEnum(target) << ", " << index << ", " << static_cast<const void*>(data) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetInteger64i_vFn != nullptr); g_driver_gl.debug_fn.glGetInteger64i_vFn(target, index, data); } @@ -3322,6 +3471,7 @@ static void GL_BINDING_CALL Debug_glGetInteger64v(GLenum pname, GL_SERVICE_LOG("glGetInteger64v" << "(" << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetInteger64vFn != nullptr); g_driver_gl.debug_fn.glGetInteger64vFn(pname, params); } @@ -3331,6 +3481,7 @@ static void GL_BINDING_CALL Debug_glGetIntegeri_v(GLenum target, GL_SERVICE_LOG("glGetIntegeri_v" << "(" << GLEnums::GetStringEnum(target) << ", " << index << ", " << static_cast<const void*>(data) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetIntegeri_vFn != nullptr); g_driver_gl.debug_fn.glGetIntegeri_vFn(target, index, data); } @@ -3338,6 +3489,7 @@ static void GL_BINDING_CALL Debug_glGetIntegerv(GLenum pname, GLint* params) { GL_SERVICE_LOG("glGetIntegerv" << "(" << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetIntegervFn != nullptr); g_driver_gl.debug_fn.glGetIntegervFn(pname, params); } @@ -3351,6 +3503,7 @@ static void GL_BINDING_CALL Debug_glGetInternalformativ(GLenum target, << GLEnums::GetStringEnum(internalformat) << ", " << GLEnums::GetStringEnum(pname) << ", " << bufSize << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetInternalformativFn != nullptr); g_driver_gl.debug_fn.glGetInternalformativFn(target, internalformat, pname, bufSize, params); } @@ -3365,6 +3518,7 @@ static void GL_BINDING_CALL Debug_glGetProgramBinary(GLuint program, << static_cast<const void*>(length) << ", " << static_cast<const void*>(binaryFormat) << ", " << static_cast<const void*>(binary) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetProgramBinaryFn != nullptr); g_driver_gl.debug_fn.glGetProgramBinaryFn(program, bufSize, length, binaryFormat, binary); } @@ -3377,6 +3531,7 @@ static void GL_BINDING_CALL Debug_glGetProgramInfoLog(GLuint program, << "(" << program << ", " << bufsize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(infolog) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetProgramInfoLogFn != nullptr); g_driver_gl.debug_fn.glGetProgramInfoLogFn(program, bufsize, length, infolog); } @@ -3390,6 +3545,7 @@ Debug_glGetProgramInterfaceiv(GLuint program, << GLEnums::GetStringEnum(programInterface) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetProgramInterfaceivFn != nullptr); g_driver_gl.debug_fn.glGetProgramInterfaceivFn(program, programInterface, pname, params); } @@ -3400,6 +3556,7 @@ static void GL_BINDING_CALL Debug_glGetProgramiv(GLuint program, GL_SERVICE_LOG("glGetProgramiv" << "(" << program << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetProgramivFn != nullptr); g_driver_gl.debug_fn.glGetProgramivFn(program, pname, params); } @@ -3418,6 +3575,7 @@ Debug_glGetProgramResourceiv(GLuint program, << ", " << propCount << ", " << static_cast<const void*>(props) << ", " << bufSize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetProgramResourceivFn != nullptr); g_driver_gl.debug_fn.glGetProgramResourceivFn(program, programInterface, index, propCount, props, bufSize, length, params); @@ -3431,6 +3589,7 @@ Debug_glGetProgramResourceLocation(GLuint program, << "(" << program << ", " << GLEnums::GetStringEnum(programInterface) << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glGetProgramResourceLocationFn != nullptr); GLint result = g_driver_gl.debug_fn.glGetProgramResourceLocationFn( program, programInterface, name); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -3449,6 +3608,7 @@ Debug_glGetProgramResourceName(GLuint program, << GLEnums::GetStringEnum(programInterface) << ", " << index << ", " << bufSize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(name) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetProgramResourceNameFn != nullptr); g_driver_gl.debug_fn.glGetProgramResourceNameFn(program, programInterface, index, bufSize, length, name); } @@ -3460,6 +3620,7 @@ static void GL_BINDING_CALL Debug_glGetQueryiv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetQueryivFn != nullptr); g_driver_gl.debug_fn.glGetQueryivFn(target, pname, params); } @@ -3469,6 +3630,7 @@ static void GL_BINDING_CALL Debug_glGetQueryObjecti64v(GLuint id, GL_SERVICE_LOG("glGetQueryObjecti64v" << "(" << id << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetQueryObjecti64vFn != nullptr); g_driver_gl.debug_fn.glGetQueryObjecti64vFn(id, pname, params); } @@ -3478,6 +3640,7 @@ static void GL_BINDING_CALL Debug_glGetQueryObjectiv(GLuint id, GL_SERVICE_LOG("glGetQueryObjectiv" << "(" << id << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetQueryObjectivFn != nullptr); g_driver_gl.debug_fn.glGetQueryObjectivFn(id, pname, params); } @@ -3487,6 +3650,7 @@ static void GL_BINDING_CALL Debug_glGetQueryObjectui64v(GLuint id, GL_SERVICE_LOG("glGetQueryObjectui64v" << "(" << id << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetQueryObjectui64vFn != nullptr); g_driver_gl.debug_fn.glGetQueryObjectui64vFn(id, pname, params); } @@ -3496,6 +3660,7 @@ static void GL_BINDING_CALL Debug_glGetQueryObjectuiv(GLuint id, GL_SERVICE_LOG("glGetQueryObjectuiv" << "(" << id << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetQueryObjectuivFn != nullptr); g_driver_gl.debug_fn.glGetQueryObjectuivFn(id, pname, params); } @@ -3507,6 +3672,7 @@ Debug_glGetRenderbufferParameterivEXT(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetRenderbufferParameterivEXTFn != nullptr); g_driver_gl.debug_fn.glGetRenderbufferParameterivEXTFn(target, pname, params); } @@ -3516,6 +3682,7 @@ static void GL_BINDING_CALL Debug_glGetSamplerParameterfv(GLuint sampler, GL_SERVICE_LOG("glGetSamplerParameterfv" << "(" << sampler << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetSamplerParameterfvFn != nullptr); g_driver_gl.debug_fn.glGetSamplerParameterfvFn(sampler, pname, params); } @@ -3525,6 +3692,7 @@ static void GL_BINDING_CALL Debug_glGetSamplerParameteriv(GLuint sampler, GL_SERVICE_LOG("glGetSamplerParameteriv" << "(" << sampler << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetSamplerParameterivFn != nullptr); g_driver_gl.debug_fn.glGetSamplerParameterivFn(sampler, pname, params); } @@ -3536,6 +3704,7 @@ static void GL_BINDING_CALL Debug_glGetShaderInfoLog(GLuint shader, << "(" << shader << ", " << bufsize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(infolog) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetShaderInfoLogFn != nullptr); g_driver_gl.debug_fn.glGetShaderInfoLogFn(shader, bufsize, length, infolog); } @@ -3545,6 +3714,7 @@ static void GL_BINDING_CALL Debug_glGetShaderiv(GLuint shader, GL_SERVICE_LOG("glGetShaderiv" << "(" << shader << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetShaderivFn != nullptr); g_driver_gl.debug_fn.glGetShaderivFn(shader, pname, params); } @@ -3558,6 +3728,7 @@ Debug_glGetShaderPrecisionFormat(GLenum shadertype, << GLEnums::GetStringEnum(precisiontype) << ", " << static_cast<const void*>(range) << ", " << static_cast<const void*>(precision) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetShaderPrecisionFormatFn != nullptr); g_driver_gl.debug_fn.glGetShaderPrecisionFormatFn(shadertype, precisiontype, range, precision); } @@ -3570,12 +3741,14 @@ static void GL_BINDING_CALL Debug_glGetShaderSource(GLuint shader, << "(" << shader << ", " << bufsize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(source) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetShaderSourceFn != nullptr); g_driver_gl.debug_fn.glGetShaderSourceFn(shader, bufsize, length, source); } static const GLubyte* GL_BINDING_CALL Debug_glGetString(GLenum name) { GL_SERVICE_LOG("glGetString" << "(" << GLEnums::GetStringEnum(name) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetStringFn != nullptr); const GLubyte* result = g_driver_gl.debug_fn.glGetStringFn(name); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3586,6 +3759,7 @@ static const GLubyte* GL_BINDING_CALL Debug_glGetStringi(GLenum name, GL_SERVICE_LOG("glGetStringi" << "(" << GLEnums::GetStringEnum(name) << ", " << index << ")"); + DCHECK(g_driver_gl.debug_fn.glGetStringiFn != nullptr); const GLubyte* result = g_driver_gl.debug_fn.glGetStringiFn(name, index); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3600,6 +3774,7 @@ static void GL_BINDING_CALL Debug_glGetSynciv(GLsync sync, << "(" << sync << ", " << GLEnums::GetStringEnum(pname) << ", " << bufSize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(values) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetSyncivFn != nullptr); g_driver_gl.debug_fn.glGetSyncivFn(sync, pname, bufSize, length, values); } @@ -3611,6 +3786,7 @@ static void GL_BINDING_CALL Debug_glGetTexLevelParameterfv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << level << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetTexLevelParameterfvFn != nullptr); g_driver_gl.debug_fn.glGetTexLevelParameterfvFn(target, level, pname, params); } @@ -3622,6 +3798,7 @@ static void GL_BINDING_CALL Debug_glGetTexLevelParameteriv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << level << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetTexLevelParameterivFn != nullptr); g_driver_gl.debug_fn.glGetTexLevelParameterivFn(target, level, pname, params); } @@ -3632,6 +3809,7 @@ static void GL_BINDING_CALL Debug_glGetTexParameterfv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetTexParameterfvFn != nullptr); g_driver_gl.debug_fn.glGetTexParameterfvFn(target, pname, params); } @@ -3642,6 +3820,7 @@ static void GL_BINDING_CALL Debug_glGetTexParameteriv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetTexParameterivFn != nullptr); g_driver_gl.debug_fn.glGetTexParameterivFn(target, pname, params); } @@ -3658,6 +3837,7 @@ static void GL_BINDING_CALL Debug_glGetTransformFeedbackVarying(GLuint program, << static_cast<const void*>(size) << ", " << static_cast<const void*>(type) << ", " << static_cast<const void*>(name) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetTransformFeedbackVaryingFn != nullptr); g_driver_gl.debug_fn.glGetTransformFeedbackVaryingFn( program, index, bufSize, length, size, type, name); } @@ -3671,6 +3851,7 @@ Debug_glGetTranslatedShaderSourceANGLE(GLuint shader, << "(" << shader << ", " << bufsize << ", " << static_cast<const void*>(length) << ", " << static_cast<const void*>(source) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetTranslatedShaderSourceANGLEFn != nullptr); g_driver_gl.debug_fn.glGetTranslatedShaderSourceANGLEFn(shader, bufsize, length, source); } @@ -3679,6 +3860,7 @@ static GLuint GL_BINDING_CALL Debug_glGetUniformBlockIndex(GLuint program, const char* uniformBlockName) { GL_SERVICE_LOG("glGetUniformBlockIndex" << "(" << program << ", " << uniformBlockName << ")"); + DCHECK(g_driver_gl.debug_fn.glGetUniformBlockIndexFn != nullptr); GLuint result = g_driver_gl.debug_fn.glGetUniformBlockIndexFn(program, uniformBlockName); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -3691,6 +3873,7 @@ static void GL_BINDING_CALL Debug_glGetUniformfv(GLuint program, GL_SERVICE_LOG("glGetUniformfv" << "(" << program << ", " << location << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetUniformfvFn != nullptr); g_driver_gl.debug_fn.glGetUniformfvFn(program, location, params); } @@ -3703,6 +3886,7 @@ Debug_glGetUniformIndices(GLuint program, << "(" << program << ", " << uniformCount << ", " << static_cast<const void*>(uniformNames) << ", " << static_cast<const void*>(uniformIndices) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetUniformIndicesFn != nullptr); g_driver_gl.debug_fn.glGetUniformIndicesFn(program, uniformCount, uniformNames, uniformIndices); } @@ -3713,6 +3897,7 @@ static void GL_BINDING_CALL Debug_glGetUniformiv(GLuint program, GL_SERVICE_LOG("glGetUniformiv" << "(" << program << ", " << location << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetUniformivFn != nullptr); g_driver_gl.debug_fn.glGetUniformivFn(program, location, params); } @@ -3720,6 +3905,7 @@ static GLint GL_BINDING_CALL Debug_glGetUniformLocation(GLuint program, const char* name) { GL_SERVICE_LOG("glGetUniformLocation" << "(" << program << ", " << name << ")"); + DCHECK(g_driver_gl.debug_fn.glGetUniformLocationFn != nullptr); GLint result = g_driver_gl.debug_fn.glGetUniformLocationFn(program, name); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3731,6 +3917,7 @@ static void GL_BINDING_CALL Debug_glGetUniformuiv(GLuint program, GL_SERVICE_LOG("glGetUniformuiv" << "(" << program << ", " << location << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetUniformuivFn != nullptr); g_driver_gl.debug_fn.glGetUniformuivFn(program, location, params); } @@ -3740,6 +3927,7 @@ static void GL_BINDING_CALL Debug_glGetVertexAttribfv(GLuint index, GL_SERVICE_LOG("glGetVertexAttribfv" << "(" << index << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetVertexAttribfvFn != nullptr); g_driver_gl.debug_fn.glGetVertexAttribfvFn(index, pname, params); } @@ -3749,6 +3937,7 @@ static void GL_BINDING_CALL Debug_glGetVertexAttribiv(GLuint index, GL_SERVICE_LOG("glGetVertexAttribiv" << "(" << index << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glGetVertexAttribivFn != nullptr); g_driver_gl.debug_fn.glGetVertexAttribivFn(index, pname, params); } @@ -3758,6 +3947,7 @@ static void GL_BINDING_CALL Debug_glGetVertexAttribPointerv(GLuint index, GL_SERVICE_LOG("glGetVertexAttribPointerv" << "(" << index << ", " << GLEnums::GetStringEnum(pname) << ", " << pointer << ")"); + DCHECK(g_driver_gl.debug_fn.glGetVertexAttribPointervFn != nullptr); g_driver_gl.debug_fn.glGetVertexAttribPointervFn(index, pname, pointer); } @@ -3765,6 +3955,7 @@ static void GL_BINDING_CALL Debug_glHint(GLenum target, GLenum mode) { GL_SERVICE_LOG("glHint" << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(mode) << ")"); + DCHECK(g_driver_gl.debug_fn.glHintFn != nullptr); g_driver_gl.debug_fn.glHintFn(target, mode); } @@ -3772,6 +3963,7 @@ static void GL_BINDING_CALL Debug_glInsertEventMarkerEXT(GLsizei length, const char* marker) { GL_SERVICE_LOG("glInsertEventMarkerEXT" << "(" << length << ", " << marker << ")"); + DCHECK(g_driver_gl.debug_fn.glInsertEventMarkerEXTFn != nullptr); g_driver_gl.debug_fn.glInsertEventMarkerEXTFn(length, marker); } @@ -3783,6 +3975,7 @@ Debug_glInvalidateFramebuffer(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << numAttachments << ", " << static_cast<const void*>(attachments) << ")"); + DCHECK(g_driver_gl.debug_fn.glInvalidateFramebufferFn != nullptr); g_driver_gl.debug_fn.glInvalidateFramebufferFn(target, numAttachments, attachments); } @@ -3800,6 +3993,7 @@ Debug_glInvalidateSubFramebuffer(GLenum target, << numAttachments << ", " << static_cast<const void*>(attachments) << ", " << x << ", " << y << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glInvalidateSubFramebufferFn != nullptr); g_driver_gl.debug_fn.glInvalidateSubFramebufferFn( target, numAttachments, attachments, x, y, width, height); } @@ -3807,6 +4001,7 @@ Debug_glInvalidateSubFramebuffer(GLenum target, static GLboolean GL_BINDING_CALL Debug_glIsBuffer(GLuint buffer) { GL_SERVICE_LOG("glIsBuffer" << "(" << buffer << ")"); + DCHECK(g_driver_gl.debug_fn.glIsBufferFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsBufferFn(buffer); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3815,6 +4010,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsBuffer(GLuint buffer) { static GLboolean GL_BINDING_CALL Debug_glIsEnabled(GLenum cap) { GL_SERVICE_LOG("glIsEnabled" << "(" << GLEnums::GetStringEnum(cap) << ")"); + DCHECK(g_driver_gl.debug_fn.glIsEnabledFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsEnabledFn(cap); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3823,6 +4019,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsEnabled(GLenum cap) { static GLboolean GL_BINDING_CALL Debug_glIsFenceAPPLE(GLuint fence) { GL_SERVICE_LOG("glIsFenceAPPLE" << "(" << fence << ")"); + DCHECK(g_driver_gl.debug_fn.glIsFenceAPPLEFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsFenceAPPLEFn(fence); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3831,6 +4028,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsFenceAPPLE(GLuint fence) { static GLboolean GL_BINDING_CALL Debug_glIsFenceNV(GLuint fence) { GL_SERVICE_LOG("glIsFenceNV" << "(" << fence << ")"); + DCHECK(g_driver_gl.debug_fn.glIsFenceNVFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsFenceNVFn(fence); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3839,6 +4037,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsFenceNV(GLuint fence) { static GLboolean GL_BINDING_CALL Debug_glIsFramebufferEXT(GLuint framebuffer) { GL_SERVICE_LOG("glIsFramebufferEXT" << "(" << framebuffer << ")"); + DCHECK(g_driver_gl.debug_fn.glIsFramebufferEXTFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsFramebufferEXTFn(framebuffer); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3847,6 +4046,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsFramebufferEXT(GLuint framebuffer) { static GLboolean GL_BINDING_CALL Debug_glIsPathNV(GLuint path) { GL_SERVICE_LOG("glIsPathNV" << "(" << path << ")"); + DCHECK(g_driver_gl.debug_fn.glIsPathNVFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsPathNVFn(path); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3855,6 +4055,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsPathNV(GLuint path) { static GLboolean GL_BINDING_CALL Debug_glIsProgram(GLuint program) { GL_SERVICE_LOG("glIsProgram" << "(" << program << ")"); + DCHECK(g_driver_gl.debug_fn.glIsProgramFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsProgramFn(program); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3863,6 +4064,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsProgram(GLuint program) { static GLboolean GL_BINDING_CALL Debug_glIsQuery(GLuint query) { GL_SERVICE_LOG("glIsQuery" << "(" << query << ")"); + DCHECK(g_driver_gl.debug_fn.glIsQueryFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsQueryFn(query); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3872,6 +4074,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsRenderbufferEXT(GLuint renderbuffer) { GL_SERVICE_LOG("glIsRenderbufferEXT" << "(" << renderbuffer << ")"); + DCHECK(g_driver_gl.debug_fn.glIsRenderbufferEXTFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsRenderbufferEXTFn(renderbuffer); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3880,6 +4083,7 @@ Debug_glIsRenderbufferEXT(GLuint renderbuffer) { static GLboolean GL_BINDING_CALL Debug_glIsSampler(GLuint sampler) { GL_SERVICE_LOG("glIsSampler" << "(" << sampler << ")"); + DCHECK(g_driver_gl.debug_fn.glIsSamplerFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsSamplerFn(sampler); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3888,6 +4092,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsSampler(GLuint sampler) { static GLboolean GL_BINDING_CALL Debug_glIsShader(GLuint shader) { GL_SERVICE_LOG("glIsShader" << "(" << shader << ")"); + DCHECK(g_driver_gl.debug_fn.glIsShaderFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsShaderFn(shader); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3896,6 +4101,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsShader(GLuint shader) { static GLboolean GL_BINDING_CALL Debug_glIsSync(GLsync sync) { GL_SERVICE_LOG("glIsSync" << "(" << sync << ")"); + DCHECK(g_driver_gl.debug_fn.glIsSyncFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsSyncFn(sync); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3904,6 +4110,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsSync(GLsync sync) { static GLboolean GL_BINDING_CALL Debug_glIsTexture(GLuint texture) { GL_SERVICE_LOG("glIsTexture" << "(" << texture << ")"); + DCHECK(g_driver_gl.debug_fn.glIsTextureFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsTextureFn(texture); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3912,6 +4119,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsTexture(GLuint texture) { static GLboolean GL_BINDING_CALL Debug_glIsTransformFeedback(GLuint id) { GL_SERVICE_LOG("glIsTransformFeedback" << "(" << id << ")"); + DCHECK(g_driver_gl.debug_fn.glIsTransformFeedbackFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsTransformFeedbackFn(id); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3920,6 +4128,7 @@ static GLboolean GL_BINDING_CALL Debug_glIsTransformFeedback(GLuint id) { static GLboolean GL_BINDING_CALL Debug_glIsVertexArrayOES(GLuint array) { GL_SERVICE_LOG("glIsVertexArrayOES" << "(" << array << ")"); + DCHECK(g_driver_gl.debug_fn.glIsVertexArrayOESFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glIsVertexArrayOESFn(array); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3928,12 +4137,14 @@ static GLboolean GL_BINDING_CALL Debug_glIsVertexArrayOES(GLuint array) { static void GL_BINDING_CALL Debug_glLineWidth(GLfloat width) { GL_SERVICE_LOG("glLineWidth" << "(" << width << ")"); + DCHECK(g_driver_gl.debug_fn.glLineWidthFn != nullptr); g_driver_gl.debug_fn.glLineWidthFn(width); } static void GL_BINDING_CALL Debug_glLinkProgram(GLuint program) { GL_SERVICE_LOG("glLinkProgram" << "(" << program << ")"); + DCHECK(g_driver_gl.debug_fn.glLinkProgramFn != nullptr); g_driver_gl.debug_fn.glLinkProgramFn(program); } @@ -3941,6 +4152,7 @@ static void* GL_BINDING_CALL Debug_glMapBuffer(GLenum target, GLenum access) { GL_SERVICE_LOG("glMapBuffer" << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(access) << ")"); + DCHECK(g_driver_gl.debug_fn.glMapBufferFn != nullptr); void* result = g_driver_gl.debug_fn.glMapBufferFn(target, access); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -3953,6 +4165,7 @@ static void* GL_BINDING_CALL Debug_glMapBufferRange(GLenum target, GL_SERVICE_LOG("glMapBufferRange" << "(" << GLEnums::GetStringEnum(target) << ", " << offset << ", " << length << ", " << access << ")"); + DCHECK(g_driver_gl.debug_fn.glMapBufferRangeFn != nullptr); void* result = g_driver_gl.debug_fn.glMapBufferRangeFn(target, offset, length, access); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -3964,18 +4177,21 @@ static void GL_BINDING_CALL Debug_glMatrixLoadfEXT(GLenum matrixMode, GL_SERVICE_LOG("glMatrixLoadfEXT" << "(" << GLEnums::GetStringEnum(matrixMode) << ", " << static_cast<const void*>(m) << ")"); + DCHECK(g_driver_gl.debug_fn.glMatrixLoadfEXTFn != nullptr); g_driver_gl.debug_fn.glMatrixLoadfEXTFn(matrixMode, m); } static void GL_BINDING_CALL Debug_glMatrixLoadIdentityEXT(GLenum matrixMode) { GL_SERVICE_LOG("glMatrixLoadIdentityEXT" << "(" << GLEnums::GetStringEnum(matrixMode) << ")"); + DCHECK(g_driver_gl.debug_fn.glMatrixLoadIdentityEXTFn != nullptr); g_driver_gl.debug_fn.glMatrixLoadIdentityEXTFn(matrixMode); } static void GL_BINDING_CALL Debug_glMemoryBarrierEXT(GLbitfield barriers) { GL_SERVICE_LOG("glMemoryBarrierEXT" << "(" << barriers << ")"); + DCHECK(g_driver_gl.debug_fn.glMemoryBarrierEXTFn != nullptr); g_driver_gl.debug_fn.glMemoryBarrierEXTFn(barriers); } @@ -3990,6 +4206,7 @@ static void GL_BINDING_CALL Debug_glPathCommandsNV(GLuint path, << static_cast<const void*>(commands) << ", " << numCoords << ", " << GLEnums::GetStringEnum(coordType) << ", " << static_cast<const void*>(coords) << ")"); + DCHECK(g_driver_gl.debug_fn.glPathCommandsNVFn != nullptr); g_driver_gl.debug_fn.glPathCommandsNVFn(path, numCommands, commands, numCoords, coordType, coords); } @@ -4000,6 +4217,7 @@ static void GL_BINDING_CALL Debug_glPathParameterfNV(GLuint path, GL_SERVICE_LOG("glPathParameterfNV" << "(" << path << ", " << GLEnums::GetStringEnum(pname) << ", " << value << ")"); + DCHECK(g_driver_gl.debug_fn.glPathParameterfNVFn != nullptr); g_driver_gl.debug_fn.glPathParameterfNVFn(path, pname, value); } @@ -4009,6 +4227,7 @@ static void GL_BINDING_CALL Debug_glPathParameteriNV(GLuint path, GL_SERVICE_LOG("glPathParameteriNV" << "(" << path << ", " << GLEnums::GetStringEnum(pname) << ", " << value << ")"); + DCHECK(g_driver_gl.debug_fn.glPathParameteriNVFn != nullptr); g_driver_gl.debug_fn.glPathParameteriNVFn(path, pname, value); } @@ -4018,6 +4237,7 @@ static void GL_BINDING_CALL Debug_glPathStencilFuncNV(GLenum func, GL_SERVICE_LOG("glPathStencilFuncNV" << "(" << GLEnums::GetStringEnum(func) << ", " << ref << ", " << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glPathStencilFuncNVFn != nullptr); g_driver_gl.debug_fn.glPathStencilFuncNVFn(func, ref, mask); } @@ -4025,6 +4245,7 @@ static void GL_BINDING_CALL Debug_glPauseTransformFeedback(void) { GL_SERVICE_LOG("glPauseTransformFeedback" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glPauseTransformFeedbackFn != nullptr); g_driver_gl.debug_fn.glPauseTransformFeedbackFn(); } @@ -4032,6 +4253,7 @@ static void GL_BINDING_CALL Debug_glPixelStorei(GLenum pname, GLint param) { GL_SERVICE_LOG("glPixelStorei" << "(" << GLEnums::GetStringEnum(pname) << ", " << param << ")"); + DCHECK(g_driver_gl.debug_fn.glPixelStoreiFn != nullptr); g_driver_gl.debug_fn.glPixelStoreiFn(pname, param); } @@ -4039,6 +4261,7 @@ static void GL_BINDING_CALL Debug_glPointParameteri(GLenum pname, GLint param) { GL_SERVICE_LOG("glPointParameteri" << "(" << GLEnums::GetStringEnum(pname) << ", " << param << ")"); + DCHECK(g_driver_gl.debug_fn.glPointParameteriFn != nullptr); g_driver_gl.debug_fn.glPointParameteriFn(pname, param); } @@ -4046,6 +4269,7 @@ static void GL_BINDING_CALL Debug_glPolygonOffset(GLfloat factor, GLfloat units) { GL_SERVICE_LOG("glPolygonOffset" << "(" << factor << ", " << units << ")"); + DCHECK(g_driver_gl.debug_fn.glPolygonOffsetFn != nullptr); g_driver_gl.debug_fn.glPolygonOffsetFn(factor, units); } @@ -4053,9 +4277,17 @@ static void GL_BINDING_CALL Debug_glPopGroupMarkerEXT(void) { GL_SERVICE_LOG("glPopGroupMarkerEXT" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glPopGroupMarkerEXTFn != nullptr); g_driver_gl.debug_fn.glPopGroupMarkerEXTFn(); } +static void GL_BINDING_CALL Debug_glPrimitiveRestartIndex(GLuint index) { + GL_SERVICE_LOG("glPrimitiveRestartIndex" + << "(" << index << ")"); + DCHECK(g_driver_gl.debug_fn.glPrimitiveRestartIndexFn != nullptr); + g_driver_gl.debug_fn.glPrimitiveRestartIndexFn(index); +} + static void GL_BINDING_CALL Debug_glProgramBinary(GLuint program, GLenum binaryFormat, const GLvoid* binary, @@ -4064,6 +4296,7 @@ static void GL_BINDING_CALL Debug_glProgramBinary(GLuint program, << "(" << program << ", " << GLEnums::GetStringEnum(binaryFormat) << ", " << static_cast<const void*>(binary) << ", " << length << ")"); + DCHECK(g_driver_gl.debug_fn.glProgramBinaryFn != nullptr); g_driver_gl.debug_fn.glProgramBinaryFn(program, binaryFormat, binary, length); } @@ -4073,6 +4306,7 @@ static void GL_BINDING_CALL Debug_glProgramParameteri(GLuint program, GL_SERVICE_LOG("glProgramParameteri" << "(" << program << ", " << GLEnums::GetStringEnum(pname) << ", " << value << ")"); + DCHECK(g_driver_gl.debug_fn.glProgramParameteriFn != nullptr); g_driver_gl.debug_fn.glProgramParameteriFn(program, pname, value); } @@ -4086,6 +4320,7 @@ Debug_glProgramPathFragmentInputGenNV(GLuint program, << "(" << program << ", " << location << ", " << GLEnums::GetStringEnum(genMode) << ", " << components << ", " << static_cast<const void*>(coeffs) << ")"); + DCHECK(g_driver_gl.debug_fn.glProgramPathFragmentInputGenNVFn != nullptr); g_driver_gl.debug_fn.glProgramPathFragmentInputGenNVFn( program, location, genMode, components, coeffs); } @@ -4094,18 +4329,21 @@ static void GL_BINDING_CALL Debug_glPushGroupMarkerEXT(GLsizei length, const char* marker) { GL_SERVICE_LOG("glPushGroupMarkerEXT" << "(" << length << ", " << marker << ")"); + DCHECK(g_driver_gl.debug_fn.glPushGroupMarkerEXTFn != nullptr); g_driver_gl.debug_fn.glPushGroupMarkerEXTFn(length, marker); } static void GL_BINDING_CALL Debug_glQueryCounter(GLuint id, GLenum target) { GL_SERVICE_LOG("glQueryCounter" << "(" << id << ", " << GLEnums::GetStringEnum(target) << ")"); + DCHECK(g_driver_gl.debug_fn.glQueryCounterFn != nullptr); g_driver_gl.debug_fn.glQueryCounterFn(id, target); } static void GL_BINDING_CALL Debug_glReadBuffer(GLenum src) { GL_SERVICE_LOG("glReadBuffer" << "(" << GLEnums::GetStringEnum(src) << ")"); + DCHECK(g_driver_gl.debug_fn.glReadBufferFn != nullptr); g_driver_gl.debug_fn.glReadBufferFn(src); } @@ -4121,6 +4359,7 @@ static void GL_BINDING_CALL Debug_glReadPixels(GLint x, << ", " << GLEnums::GetStringEnum(format) << ", " << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(pixels) << ")"); + DCHECK(g_driver_gl.debug_fn.glReadPixelsFn != nullptr); g_driver_gl.debug_fn.glReadPixelsFn(x, y, width, height, format, type, pixels); } @@ -4129,6 +4368,7 @@ static void GL_BINDING_CALL Debug_glReleaseShaderCompiler(void) { GL_SERVICE_LOG("glReleaseShaderCompiler" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glReleaseShaderCompilerFn != nullptr); g_driver_gl.debug_fn.glReleaseShaderCompilerFn(); } @@ -4141,6 +4381,7 @@ Debug_glRenderbufferStorageEXT(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glRenderbufferStorageEXTFn != nullptr); g_driver_gl.debug_fn.glRenderbufferStorageEXTFn(target, internalformat, width, height); } @@ -4155,6 +4396,7 @@ Debug_glRenderbufferStorageMultisample(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << samples << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glRenderbufferStorageMultisampleFn != nullptr); g_driver_gl.debug_fn.glRenderbufferStorageMultisampleFn( target, samples, internalformat, width, height); } @@ -4169,6 +4411,8 @@ Debug_glRenderbufferStorageMultisampleANGLE(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << samples << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glRenderbufferStorageMultisampleANGLEFn != + nullptr); g_driver_gl.debug_fn.glRenderbufferStorageMultisampleANGLEFn( target, samples, internalformat, width, height); } @@ -4183,6 +4427,7 @@ Debug_glRenderbufferStorageMultisampleEXT(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << samples << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glRenderbufferStorageMultisampleEXTFn != nullptr); g_driver_gl.debug_fn.glRenderbufferStorageMultisampleEXTFn( target, samples, internalformat, width, height); } @@ -4197,6 +4442,7 @@ Debug_glRenderbufferStorageMultisampleIMG(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << samples << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glRenderbufferStorageMultisampleIMGFn != nullptr); g_driver_gl.debug_fn.glRenderbufferStorageMultisampleIMGFn( target, samples, internalformat, width, height); } @@ -4205,6 +4451,7 @@ static void GL_BINDING_CALL Debug_glResumeTransformFeedback(void) { GL_SERVICE_LOG("glResumeTransformFeedback" << "(" << ")"); + DCHECK(g_driver_gl.debug_fn.glResumeTransformFeedbackFn != nullptr); g_driver_gl.debug_fn.glResumeTransformFeedbackFn(); } @@ -4213,6 +4460,7 @@ static void GL_BINDING_CALL Debug_glSampleCoverage(GLclampf value, GL_SERVICE_LOG("glSampleCoverage" << "(" << value << ", " << GLEnums::GetStringBool(invert) << ")"); + DCHECK(g_driver_gl.debug_fn.glSampleCoverageFn != nullptr); g_driver_gl.debug_fn.glSampleCoverageFn(value, invert); } @@ -4222,6 +4470,7 @@ static void GL_BINDING_CALL Debug_glSamplerParameterf(GLuint sampler, GL_SERVICE_LOG("glSamplerParameterf" << "(" << sampler << ", " << GLEnums::GetStringEnum(pname) << ", " << param << ")"); + DCHECK(g_driver_gl.debug_fn.glSamplerParameterfFn != nullptr); g_driver_gl.debug_fn.glSamplerParameterfFn(sampler, pname, param); } @@ -4231,6 +4480,7 @@ static void GL_BINDING_CALL Debug_glSamplerParameterfv(GLuint sampler, GL_SERVICE_LOG("glSamplerParameterfv" << "(" << sampler << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glSamplerParameterfvFn != nullptr); g_driver_gl.debug_fn.glSamplerParameterfvFn(sampler, pname, params); } @@ -4240,6 +4490,7 @@ static void GL_BINDING_CALL Debug_glSamplerParameteri(GLuint sampler, GL_SERVICE_LOG("glSamplerParameteri" << "(" << sampler << ", " << GLEnums::GetStringEnum(pname) << ", " << param << ")"); + DCHECK(g_driver_gl.debug_fn.glSamplerParameteriFn != nullptr); g_driver_gl.debug_fn.glSamplerParameteriFn(sampler, pname, param); } @@ -4249,6 +4500,7 @@ static void GL_BINDING_CALL Debug_glSamplerParameteriv(GLuint sampler, GL_SERVICE_LOG("glSamplerParameteriv" << "(" << sampler << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glSamplerParameterivFn != nullptr); g_driver_gl.debug_fn.glSamplerParameterivFn(sampler, pname, params); } @@ -4259,12 +4511,14 @@ static void GL_BINDING_CALL Debug_glScissor(GLint x, GL_SERVICE_LOG("glScissor" << "(" << x << ", " << y << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glScissorFn != nullptr); g_driver_gl.debug_fn.glScissorFn(x, y, width, height); } static void GL_BINDING_CALL Debug_glSetFenceAPPLE(GLuint fence) { GL_SERVICE_LOG("glSetFenceAPPLE" << "(" << fence << ")"); + DCHECK(g_driver_gl.debug_fn.glSetFenceAPPLEFn != nullptr); g_driver_gl.debug_fn.glSetFenceAPPLEFn(fence); } @@ -4272,6 +4526,7 @@ static void GL_BINDING_CALL Debug_glSetFenceNV(GLuint fence, GLenum condition) { GL_SERVICE_LOG("glSetFenceNV" << "(" << fence << ", " << GLEnums::GetStringEnum(condition) << ")"); + DCHECK(g_driver_gl.debug_fn.glSetFenceNVFn != nullptr); g_driver_gl.debug_fn.glSetFenceNVFn(fence, condition); } @@ -4284,6 +4539,7 @@ static void GL_BINDING_CALL Debug_glShaderBinary(GLsizei n, << "(" << n << ", " << static_cast<const void*>(shaders) << ", " << GLEnums::GetStringEnum(binaryformat) << ", " << static_cast<const void*>(binary) << ", " << length << ")"); + DCHECK(g_driver_gl.debug_fn.glShaderBinaryFn != nullptr); g_driver_gl.debug_fn.glShaderBinaryFn(n, shaders, binaryformat, binary, length); } @@ -4296,6 +4552,7 @@ static void GL_BINDING_CALL Debug_glShaderSource(GLuint shader, << "(" << shader << ", " << count << ", " << static_cast<const void*>(str) << ", " << static_cast<const void*>(length) << ")"); + DCHECK(g_driver_gl.debug_fn.glShaderSourceFn != nullptr); g_driver_gl.debug_fn.glShaderSourceFn(shader, count, str, length); GL_SERVICE_LOG_CODE_BLOCK({ @@ -4330,6 +4587,7 @@ Debug_glStencilFillPathInstancedNV(GLsizei numPaths, << GLEnums::GetStringEnum(fillMode) << ", " << mask << ", " << GLEnums::GetStringEnum(transformType) << ", " << static_cast<const void*>(transformValues) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilFillPathInstancedNVFn != nullptr); g_driver_gl.debug_fn.glStencilFillPathInstancedNVFn( numPaths, pathNameType, paths, pathBase, fillMode, mask, transformType, transformValues); @@ -4341,6 +4599,7 @@ static void GL_BINDING_CALL Debug_glStencilFillPathNV(GLuint path, GL_SERVICE_LOG("glStencilFillPathNV" << "(" << path << ", " << GLEnums::GetStringEnum(fillMode) << ", " << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilFillPathNVFn != nullptr); g_driver_gl.debug_fn.glStencilFillPathNVFn(path, fillMode, mask); } @@ -4350,6 +4609,7 @@ static void GL_BINDING_CALL Debug_glStencilFunc(GLenum func, GL_SERVICE_LOG("glStencilFunc" << "(" << GLEnums::GetStringEnum(func) << ", " << ref << ", " << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilFuncFn != nullptr); g_driver_gl.debug_fn.glStencilFuncFn(func, ref, mask); } @@ -4361,12 +4621,14 @@ static void GL_BINDING_CALL Debug_glStencilFuncSeparate(GLenum face, << "(" << GLEnums::GetStringEnum(face) << ", " << GLEnums::GetStringEnum(func) << ", " << ref << ", " << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilFuncSeparateFn != nullptr); g_driver_gl.debug_fn.glStencilFuncSeparateFn(face, func, ref, mask); } static void GL_BINDING_CALL Debug_glStencilMask(GLuint mask) { GL_SERVICE_LOG("glStencilMask" << "(" << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilMaskFn != nullptr); g_driver_gl.debug_fn.glStencilMaskFn(mask); } @@ -4374,6 +4636,7 @@ static void GL_BINDING_CALL Debug_glStencilMaskSeparate(GLenum face, GLuint mask) { GL_SERVICE_LOG("glStencilMaskSeparate" << "(" << GLEnums::GetStringEnum(face) << ", " << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilMaskSeparateFn != nullptr); g_driver_gl.debug_fn.glStencilMaskSeparateFn(face, mask); } @@ -4384,6 +4647,7 @@ static void GL_BINDING_CALL Debug_glStencilOp(GLenum fail, << "(" << GLEnums::GetStringEnum(fail) << ", " << GLEnums::GetStringEnum(zfail) << ", " << GLEnums::GetStringEnum(zpass) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilOpFn != nullptr); g_driver_gl.debug_fn.glStencilOpFn(fail, zfail, zpass); } @@ -4396,6 +4660,7 @@ static void GL_BINDING_CALL Debug_glStencilOpSeparate(GLenum face, << GLEnums::GetStringEnum(fail) << ", " << GLEnums::GetStringEnum(zfail) << ", " << GLEnums::GetStringEnum(zpass) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilOpSeparateFn != nullptr); g_driver_gl.debug_fn.glStencilOpSeparateFn(face, fail, zfail, zpass); } @@ -4414,6 +4679,7 @@ Debug_glStencilStrokePathInstancedNV(GLsizei numPaths, << static_cast<const void*>(paths) << ", " << pathBase << ", " << ref << ", " << mask << ", " << GLEnums::GetStringEnum(transformType) << ", " << static_cast<const void*>(transformValues) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilStrokePathInstancedNVFn != nullptr); g_driver_gl.debug_fn.glStencilStrokePathInstancedNVFn( numPaths, pathNameType, paths, pathBase, ref, mask, transformType, transformValues); @@ -4424,6 +4690,7 @@ static void GL_BINDING_CALL Debug_glStencilStrokePathNV(GLuint path, GLuint mask) { GL_SERVICE_LOG("glStencilStrokePathNV" << "(" << path << ", " << reference << ", " << mask << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilStrokePathNVFn != nullptr); g_driver_gl.debug_fn.glStencilStrokePathNVFn(path, reference, mask); } @@ -4445,6 +4712,8 @@ Debug_glStencilThenCoverFillPathInstancedNV(GLsizei numPaths, << GLEnums::GetStringEnum(coverMode) << ", " << GLEnums::GetStringEnum(transformType) << ", " << static_cast<const void*>(transformValues) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilThenCoverFillPathInstancedNVFn != + nullptr); g_driver_gl.debug_fn.glStencilThenCoverFillPathInstancedNVFn( numPaths, pathNameType, paths, pathBase, fillMode, mask, coverMode, transformType, transformValues); @@ -4459,6 +4728,7 @@ Debug_glStencilThenCoverFillPathNV(GLuint path, << "(" << path << ", " << GLEnums::GetStringEnum(fillMode) << ", " << mask << ", " << GLEnums::GetStringEnum(coverMode) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilThenCoverFillPathNVFn != nullptr); g_driver_gl.debug_fn.glStencilThenCoverFillPathNVFn(path, fillMode, mask, coverMode); } @@ -4480,6 +4750,8 @@ Debug_glStencilThenCoverStrokePathInstancedNV(GLsizei numPaths, << ", " << mask << ", " << GLEnums::GetStringEnum(coverMode) << ", " << GLEnums::GetStringEnum(transformType) << ", " << static_cast<const void*>(transformValues) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilThenCoverStrokePathInstancedNVFn != + nullptr); g_driver_gl.debug_fn.glStencilThenCoverStrokePathInstancedNVFn( numPaths, pathNameType, paths, pathBase, ref, mask, coverMode, transformType, transformValues); @@ -4493,6 +4765,7 @@ Debug_glStencilThenCoverStrokePathNV(GLuint path, GL_SERVICE_LOG("glStencilThenCoverStrokePathNV" << "(" << path << ", " << reference << ", " << mask << ", " << GLEnums::GetStringEnum(coverMode) << ")"); + DCHECK(g_driver_gl.debug_fn.glStencilThenCoverStrokePathNVFn != nullptr); g_driver_gl.debug_fn.glStencilThenCoverStrokePathNVFn(path, reference, mask, coverMode); } @@ -4500,6 +4773,7 @@ Debug_glStencilThenCoverStrokePathNV(GLuint path, static GLboolean GL_BINDING_CALL Debug_glTestFenceAPPLE(GLuint fence) { GL_SERVICE_LOG("glTestFenceAPPLE" << "(" << fence << ")"); + DCHECK(g_driver_gl.debug_fn.glTestFenceAPPLEFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glTestFenceAPPLEFn(fence); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -4508,6 +4782,7 @@ static GLboolean GL_BINDING_CALL Debug_glTestFenceAPPLE(GLuint fence) { static GLboolean GL_BINDING_CALL Debug_glTestFenceNV(GLuint fence) { GL_SERVICE_LOG("glTestFenceNV" << "(" << fence << ")"); + DCHECK(g_driver_gl.debug_fn.glTestFenceNVFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glTestFenceNVFn(fence); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -4528,6 +4803,7 @@ static void GL_BINDING_CALL Debug_glTexImage2D(GLenum target, << ", " << border << ", " << GLEnums::GetStringEnum(format) << ", " << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(pixels) << ")"); + DCHECK(g_driver_gl.debug_fn.glTexImage2DFn != nullptr); g_driver_gl.debug_fn.glTexImage2DFn(target, level, internalformat, width, height, border, format, type, pixels); } @@ -4549,6 +4825,7 @@ static void GL_BINDING_CALL Debug_glTexImage3D(GLenum target, << GLEnums::GetStringEnum(format) << ", " << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(pixels) << ")"); + DCHECK(g_driver_gl.debug_fn.glTexImage3DFn != nullptr); g_driver_gl.debug_fn.glTexImage3DFn(target, level, internalformat, width, height, depth, border, format, type, pixels); @@ -4560,6 +4837,7 @@ static void GL_BINDING_CALL Debug_glTexParameterf(GLenum target, GL_SERVICE_LOG("glTexParameterf" << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << param << ")"); + DCHECK(g_driver_gl.debug_fn.glTexParameterfFn != nullptr); g_driver_gl.debug_fn.glTexParameterfFn(target, pname, param); } @@ -4570,6 +4848,7 @@ static void GL_BINDING_CALL Debug_glTexParameterfv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glTexParameterfvFn != nullptr); g_driver_gl.debug_fn.glTexParameterfvFn(target, pname, params); } @@ -4579,6 +4858,7 @@ static void GL_BINDING_CALL Debug_glTexParameteri(GLenum target, GL_SERVICE_LOG("glTexParameteri" << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << param << ")"); + DCHECK(g_driver_gl.debug_fn.glTexParameteriFn != nullptr); g_driver_gl.debug_fn.glTexParameteriFn(target, pname, param); } @@ -4589,6 +4869,7 @@ static void GL_BINDING_CALL Debug_glTexParameteriv(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << GLEnums::GetStringEnum(pname) << ", " << static_cast<const void*>(params) << ")"); + DCHECK(g_driver_gl.debug_fn.glTexParameterivFn != nullptr); g_driver_gl.debug_fn.glTexParameterivFn(target, pname, params); } @@ -4601,6 +4882,7 @@ static void GL_BINDING_CALL Debug_glTexStorage2DEXT(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << levels << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glTexStorage2DEXTFn != nullptr); g_driver_gl.debug_fn.glTexStorage2DEXTFn(target, levels, internalformat, width, height); } @@ -4615,6 +4897,7 @@ static void GL_BINDING_CALL Debug_glTexStorage3D(GLenum target, << "(" << GLEnums::GetStringEnum(target) << ", " << levels << ", " << GLEnums::GetStringEnum(internalformat) << ", " << width << ", " << height << ", " << depth << ")"); + DCHECK(g_driver_gl.debug_fn.glTexStorage3DFn != nullptr); g_driver_gl.debug_fn.glTexStorage3DFn(target, levels, internalformat, width, height, depth); } @@ -4634,6 +4917,7 @@ static void GL_BINDING_CALL Debug_glTexSubImage2D(GLenum target, << height << ", " << GLEnums::GetStringEnum(format) << ", " << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(pixels) << ")"); + DCHECK(g_driver_gl.debug_fn.glTexSubImage2DFn != nullptr); g_driver_gl.debug_fn.glTexSubImage2DFn(target, level, xoffset, yoffset, width, height, format, type, pixels); } @@ -4656,6 +4940,7 @@ static void GL_BINDING_CALL Debug_glTexSubImage3D(GLenum target, << GLEnums::GetStringEnum(format) << ", " << GLEnums::GetStringEnum(type) << ", " << static_cast<const void*>(pixels) << ")"); + DCHECK(g_driver_gl.debug_fn.glTexSubImage3DFn != nullptr); g_driver_gl.debug_fn.glTexSubImage3DFn(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); @@ -4670,6 +4955,7 @@ Debug_glTransformFeedbackVaryings(GLuint program, << "(" << program << ", " << count << ", " << static_cast<const void*>(varyings) << ", " << GLEnums::GetStringEnum(bufferMode) << ")"); + DCHECK(g_driver_gl.debug_fn.glTransformFeedbackVaryingsFn != nullptr); g_driver_gl.debug_fn.glTransformFeedbackVaryingsFn(program, count, varyings, bufferMode); } @@ -4677,6 +4963,7 @@ Debug_glTransformFeedbackVaryings(GLuint program, static void GL_BINDING_CALL Debug_glUniform1f(GLint location, GLfloat x) { GL_SERVICE_LOG("glUniform1f" << "(" << location << ", " << x << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform1fFn != nullptr); g_driver_gl.debug_fn.glUniform1fFn(location, x); } @@ -4686,12 +4973,14 @@ static void GL_BINDING_CALL Debug_glUniform1fv(GLint location, GL_SERVICE_LOG("glUniform1fv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform1fvFn != nullptr); g_driver_gl.debug_fn.glUniform1fvFn(location, count, v); } static void GL_BINDING_CALL Debug_glUniform1i(GLint location, GLint x) { GL_SERVICE_LOG("glUniform1i" << "(" << location << ", " << x << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform1iFn != nullptr); g_driver_gl.debug_fn.glUniform1iFn(location, x); } @@ -4701,12 +4990,14 @@ static void GL_BINDING_CALL Debug_glUniform1iv(GLint location, GL_SERVICE_LOG("glUniform1iv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform1ivFn != nullptr); g_driver_gl.debug_fn.glUniform1ivFn(location, count, v); } static void GL_BINDING_CALL Debug_glUniform1ui(GLint location, GLuint v0) { GL_SERVICE_LOG("glUniform1ui" << "(" << location << ", " << v0 << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform1uiFn != nullptr); g_driver_gl.debug_fn.glUniform1uiFn(location, v0); } @@ -4716,6 +5007,7 @@ static void GL_BINDING_CALL Debug_glUniform1uiv(GLint location, GL_SERVICE_LOG("glUniform1uiv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform1uivFn != nullptr); g_driver_gl.debug_fn.glUniform1uivFn(location, count, v); } @@ -4724,6 +5016,7 @@ static void GL_BINDING_CALL Debug_glUniform2f(GLint location, GLfloat y) { GL_SERVICE_LOG("glUniform2f" << "(" << location << ", " << x << ", " << y << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform2fFn != nullptr); g_driver_gl.debug_fn.glUniform2fFn(location, x, y); } @@ -4733,6 +5026,7 @@ static void GL_BINDING_CALL Debug_glUniform2fv(GLint location, GL_SERVICE_LOG("glUniform2fv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform2fvFn != nullptr); g_driver_gl.debug_fn.glUniform2fvFn(location, count, v); } @@ -4741,6 +5035,7 @@ static void GL_BINDING_CALL Debug_glUniform2i(GLint location, GLint y) { GL_SERVICE_LOG("glUniform2i" << "(" << location << ", " << x << ", " << y << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform2iFn != nullptr); g_driver_gl.debug_fn.glUniform2iFn(location, x, y); } @@ -4750,6 +5045,7 @@ static void GL_BINDING_CALL Debug_glUniform2iv(GLint location, GL_SERVICE_LOG("glUniform2iv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform2ivFn != nullptr); g_driver_gl.debug_fn.glUniform2ivFn(location, count, v); } @@ -4758,6 +5054,7 @@ static void GL_BINDING_CALL Debug_glUniform2ui(GLint location, GLuint v1) { GL_SERVICE_LOG("glUniform2ui" << "(" << location << ", " << v0 << ", " << v1 << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform2uiFn != nullptr); g_driver_gl.debug_fn.glUniform2uiFn(location, v0, v1); } @@ -4767,6 +5064,7 @@ static void GL_BINDING_CALL Debug_glUniform2uiv(GLint location, GL_SERVICE_LOG("glUniform2uiv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform2uivFn != nullptr); g_driver_gl.debug_fn.glUniform2uivFn(location, count, v); } @@ -4777,6 +5075,7 @@ static void GL_BINDING_CALL Debug_glUniform3f(GLint location, GL_SERVICE_LOG("glUniform3f" << "(" << location << ", " << x << ", " << y << ", " << z << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform3fFn != nullptr); g_driver_gl.debug_fn.glUniform3fFn(location, x, y, z); } @@ -4786,6 +5085,7 @@ static void GL_BINDING_CALL Debug_glUniform3fv(GLint location, GL_SERVICE_LOG("glUniform3fv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform3fvFn != nullptr); g_driver_gl.debug_fn.glUniform3fvFn(location, count, v); } @@ -4796,6 +5096,7 @@ static void GL_BINDING_CALL Debug_glUniform3i(GLint location, GL_SERVICE_LOG("glUniform3i" << "(" << location << ", " << x << ", " << y << ", " << z << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform3iFn != nullptr); g_driver_gl.debug_fn.glUniform3iFn(location, x, y, z); } @@ -4805,6 +5106,7 @@ static void GL_BINDING_CALL Debug_glUniform3iv(GLint location, GL_SERVICE_LOG("glUniform3iv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform3ivFn != nullptr); g_driver_gl.debug_fn.glUniform3ivFn(location, count, v); } @@ -4815,6 +5117,7 @@ static void GL_BINDING_CALL Debug_glUniform3ui(GLint location, GL_SERVICE_LOG("glUniform3ui" << "(" << location << ", " << v0 << ", " << v1 << ", " << v2 << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform3uiFn != nullptr); g_driver_gl.debug_fn.glUniform3uiFn(location, v0, v1, v2); } @@ -4824,6 +5127,7 @@ static void GL_BINDING_CALL Debug_glUniform3uiv(GLint location, GL_SERVICE_LOG("glUniform3uiv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform3uivFn != nullptr); g_driver_gl.debug_fn.glUniform3uivFn(location, count, v); } @@ -4832,6 +5136,7 @@ Debug_glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { GL_SERVICE_LOG("glUniform4f" << "(" << location << ", " << x << ", " << y << ", " << z << ", " << w << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform4fFn != nullptr); g_driver_gl.debug_fn.glUniform4fFn(location, x, y, z, w); } @@ -4841,6 +5146,7 @@ static void GL_BINDING_CALL Debug_glUniform4fv(GLint location, GL_SERVICE_LOG("glUniform4fv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform4fvFn != nullptr); g_driver_gl.debug_fn.glUniform4fvFn(location, count, v); } @@ -4849,6 +5155,7 @@ Debug_glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) { GL_SERVICE_LOG("glUniform4i" << "(" << location << ", " << x << ", " << y << ", " << z << ", " << w << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform4iFn != nullptr); g_driver_gl.debug_fn.glUniform4iFn(location, x, y, z, w); } @@ -4858,6 +5165,7 @@ static void GL_BINDING_CALL Debug_glUniform4iv(GLint location, GL_SERVICE_LOG("glUniform4iv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform4ivFn != nullptr); g_driver_gl.debug_fn.glUniform4ivFn(location, count, v); } @@ -4866,6 +5174,7 @@ Debug_glUniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { GL_SERVICE_LOG("glUniform4ui" << "(" << location << ", " << v0 << ", " << v1 << ", " << v2 << ", " << v3 << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform4uiFn != nullptr); g_driver_gl.debug_fn.glUniform4uiFn(location, v0, v1, v2, v3); } @@ -4875,6 +5184,7 @@ static void GL_BINDING_CALL Debug_glUniform4uiv(GLint location, GL_SERVICE_LOG("glUniform4uiv" << "(" << location << ", " << count << ", " << static_cast<const void*>(v) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniform4uivFn != nullptr); g_driver_gl.debug_fn.glUniform4uivFn(location, count, v); } @@ -4885,6 +5195,7 @@ Debug_glUniformBlockBinding(GLuint program, GL_SERVICE_LOG("glUniformBlockBinding" << "(" << program << ", " << uniformBlockIndex << ", " << uniformBlockBinding << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformBlockBindingFn != nullptr); g_driver_gl.debug_fn.glUniformBlockBindingFn(program, uniformBlockIndex, uniformBlockBinding); } @@ -4897,6 +5208,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix2fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix2fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix2fvFn(location, count, transpose, value); } @@ -4908,6 +5220,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix2x3fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix2x3fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix2x3fvFn(location, count, transpose, value); } @@ -4920,6 +5233,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix2x4fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix2x4fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix2x4fvFn(location, count, transpose, value); } @@ -4932,6 +5246,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix3fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix3fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix3fvFn(location, count, transpose, value); } @@ -4943,6 +5258,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix3x2fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix3x2fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix3x2fvFn(location, count, transpose, value); } @@ -4955,6 +5271,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix3x4fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix3x4fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix3x4fvFn(location, count, transpose, value); } @@ -4967,6 +5284,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix4fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix4fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix4fvFn(location, count, transpose, value); } @@ -4978,6 +5296,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix4x2fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix4x2fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix4x2fvFn(location, count, transpose, value); } @@ -4990,6 +5309,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix4x3fv(GLint location, << "(" << location << ", " << count << ", " << GLEnums::GetStringBool(transpose) << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_gl.debug_fn.glUniformMatrix4x3fvFn != nullptr); g_driver_gl.debug_fn.glUniformMatrix4x3fvFn(location, count, transpose, value); } @@ -4997,6 +5317,7 @@ static void GL_BINDING_CALL Debug_glUniformMatrix4x3fv(GLint location, static GLboolean GL_BINDING_CALL Debug_glUnmapBuffer(GLenum target) { GL_SERVICE_LOG("glUnmapBuffer" << "(" << GLEnums::GetStringEnum(target) << ")"); + DCHECK(g_driver_gl.debug_fn.glUnmapBufferFn != nullptr); GLboolean result = g_driver_gl.debug_fn.glUnmapBufferFn(target); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -5005,18 +5326,21 @@ static GLboolean GL_BINDING_CALL Debug_glUnmapBuffer(GLenum target) { static void GL_BINDING_CALL Debug_glUseProgram(GLuint program) { GL_SERVICE_LOG("glUseProgram" << "(" << program << ")"); + DCHECK(g_driver_gl.debug_fn.glUseProgramFn != nullptr); g_driver_gl.debug_fn.glUseProgramFn(program); } static void GL_BINDING_CALL Debug_glValidateProgram(GLuint program) { GL_SERVICE_LOG("glValidateProgram" << "(" << program << ")"); + DCHECK(g_driver_gl.debug_fn.glValidateProgramFn != nullptr); g_driver_gl.debug_fn.glValidateProgramFn(program); } static void GL_BINDING_CALL Debug_glVertexAttrib1f(GLuint indx, GLfloat x) { GL_SERVICE_LOG("glVertexAttrib1f" << "(" << indx << ", " << x << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib1fFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib1fFn(indx, x); } @@ -5025,6 +5349,7 @@ static void GL_BINDING_CALL Debug_glVertexAttrib1fv(GLuint indx, GL_SERVICE_LOG("glVertexAttrib1fv" << "(" << indx << ", " << static_cast<const void*>(values) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib1fvFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib1fvFn(indx, values); } @@ -5033,6 +5358,7 @@ static void GL_BINDING_CALL Debug_glVertexAttrib2f(GLuint indx, GLfloat y) { GL_SERVICE_LOG("glVertexAttrib2f" << "(" << indx << ", " << x << ", " << y << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib2fFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib2fFn(indx, x, y); } @@ -5041,6 +5367,7 @@ static void GL_BINDING_CALL Debug_glVertexAttrib2fv(GLuint indx, GL_SERVICE_LOG("glVertexAttrib2fv" << "(" << indx << ", " << static_cast<const void*>(values) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib2fvFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib2fvFn(indx, values); } @@ -5050,6 +5377,7 @@ static void GL_BINDING_CALL Debug_glVertexAttrib3f(GLuint indx, GLfloat z) { GL_SERVICE_LOG("glVertexAttrib3f" << "(" << indx << ", " << x << ", " << y << ", " << z << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib3fFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib3fFn(indx, x, y, z); } @@ -5058,6 +5386,7 @@ static void GL_BINDING_CALL Debug_glVertexAttrib3fv(GLuint indx, GL_SERVICE_LOG("glVertexAttrib3fv" << "(" << indx << ", " << static_cast<const void*>(values) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib3fvFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib3fvFn(indx, values); } @@ -5069,6 +5398,7 @@ static void GL_BINDING_CALL Debug_glVertexAttrib4f(GLuint indx, GL_SERVICE_LOG("glVertexAttrib4f" << "(" << indx << ", " << x << ", " << y << ", " << z << ", " << w << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib4fFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib4fFn(indx, x, y, z, w); } @@ -5077,6 +5407,7 @@ static void GL_BINDING_CALL Debug_glVertexAttrib4fv(GLuint indx, GL_SERVICE_LOG("glVertexAttrib4fv" << "(" << indx << ", " << static_cast<const void*>(values) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttrib4fvFn != nullptr); g_driver_gl.debug_fn.glVertexAttrib4fvFn(indx, values); } @@ -5084,6 +5415,7 @@ static void GL_BINDING_CALL Debug_glVertexAttribDivisorANGLE(GLuint index, GLuint divisor) { GL_SERVICE_LOG("glVertexAttribDivisorANGLE" << "(" << index << ", " << divisor << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttribDivisorANGLEFn != nullptr); g_driver_gl.debug_fn.glVertexAttribDivisorANGLEFn(index, divisor); } @@ -5092,6 +5424,7 @@ Debug_glVertexAttribI4i(GLuint indx, GLint x, GLint y, GLint z, GLint w) { GL_SERVICE_LOG("glVertexAttribI4i" << "(" << indx << ", " << x << ", " << y << ", " << z << ", " << w << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttribI4iFn != nullptr); g_driver_gl.debug_fn.glVertexAttribI4iFn(indx, x, y, z, w); } @@ -5100,6 +5433,7 @@ static void GL_BINDING_CALL Debug_glVertexAttribI4iv(GLuint indx, GL_SERVICE_LOG("glVertexAttribI4iv" << "(" << indx << ", " << static_cast<const void*>(values) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttribI4ivFn != nullptr); g_driver_gl.debug_fn.glVertexAttribI4ivFn(indx, values); } @@ -5108,6 +5442,7 @@ Debug_glVertexAttribI4ui(GLuint indx, GLuint x, GLuint y, GLuint z, GLuint w) { GL_SERVICE_LOG("glVertexAttribI4ui" << "(" << indx << ", " << x << ", " << y << ", " << z << ", " << w << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttribI4uiFn != nullptr); g_driver_gl.debug_fn.glVertexAttribI4uiFn(indx, x, y, z, w); } @@ -5116,6 +5451,7 @@ static void GL_BINDING_CALL Debug_glVertexAttribI4uiv(GLuint indx, GL_SERVICE_LOG("glVertexAttribI4uiv" << "(" << indx << ", " << static_cast<const void*>(values) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttribI4uivFn != nullptr); g_driver_gl.debug_fn.glVertexAttribI4uivFn(indx, values); } @@ -5128,6 +5464,7 @@ static void GL_BINDING_CALL Debug_glVertexAttribIPointer(GLuint indx, << "(" << indx << ", " << size << ", " << GLEnums::GetStringEnum(type) << ", " << stride << ", " << static_cast<const void*>(ptr) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttribIPointerFn != nullptr); g_driver_gl.debug_fn.glVertexAttribIPointerFn(indx, size, type, stride, ptr); } @@ -5142,6 +5479,7 @@ static void GL_BINDING_CALL Debug_glVertexAttribPointer(GLuint indx, << GLEnums::GetStringEnum(type) << ", " << GLEnums::GetStringBool(normalized) << ", " << stride << ", " << static_cast<const void*>(ptr) << ")"); + DCHECK(g_driver_gl.debug_fn.glVertexAttribPointerFn != nullptr); g_driver_gl.debug_fn.glVertexAttribPointerFn(indx, size, type, normalized, stride, ptr); } @@ -5153,6 +5491,7 @@ static void GL_BINDING_CALL Debug_glViewport(GLint x, GL_SERVICE_LOG("glViewport" << "(" << x << ", " << y << ", " << width << ", " << height << ")"); + DCHECK(g_driver_gl.debug_fn.glViewportFn != nullptr); g_driver_gl.debug_fn.glViewportFn(x, y, width, height); } @@ -5161,6 +5500,7 @@ static GLenum GL_BINDING_CALL Debug_glWaitSync(GLsync sync, GLuint64 timeout) { GL_SERVICE_LOG("glWaitSync" << "(" << sync << ", " << flags << ", " << timeout << ")"); + DCHECK(g_driver_gl.debug_fn.glWaitSyncFn != nullptr); GLenum result = g_driver_gl.debug_fn.glWaitSyncFn(sync, flags, timeout); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -6024,6 +6364,10 @@ void DriverGL::InitializeDebugBindings() { debug_fn.glPopGroupMarkerEXTFn = fn.glPopGroupMarkerEXTFn; fn.glPopGroupMarkerEXTFn = Debug_glPopGroupMarkerEXT; } + if (!debug_fn.glPrimitiveRestartIndexFn) { + debug_fn.glPrimitiveRestartIndexFn = fn.glPrimitiveRestartIndexFn; + fn.glPrimitiveRestartIndexFn = Debug_glPrimitiveRestartIndex; + } if (!debug_fn.glProgramBinaryFn) { debug_fn.glProgramBinaryFn = fn.glProgramBinaryFn; fn.glProgramBinaryFn = Debug_glProgramBinary; @@ -7673,6 +8017,10 @@ void GLApiBase::glPopGroupMarkerEXTFn(void) { driver_->fn.glPopGroupMarkerEXTFn(); } +void GLApiBase::glPrimitiveRestartIndexFn(GLuint index) { + driver_->fn.glPrimitiveRestartIndexFn(index); +} + void GLApiBase::glProgramBinaryFn(GLuint program, GLenum binaryFormat, const GLvoid* binary, @@ -9786,6 +10134,11 @@ void TraceGLApi::glPopGroupMarkerEXTFn(void) { gl_api_->glPopGroupMarkerEXTFn(); } +void TraceGLApi::glPrimitiveRestartIndexFn(GLuint index) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glPrimitiveRestartIndex") + gl_api_->glPrimitiveRestartIndexFn(index); +} + void TraceGLApi::glProgramBinaryFn(GLuint program, GLenum binaryFormat, const GLvoid* binary, @@ -12251,6 +12604,13 @@ void NoContextGLApi::glPopGroupMarkerEXTFn(void) { << "Trying to call glPopGroupMarkerEXT() without current GL context"; } +void NoContextGLApi::glPrimitiveRestartIndexFn(GLuint index) { + NOTREACHED() + << "Trying to call glPrimitiveRestartIndex() without current GL context"; + LOG(ERROR) + << "Trying to call glPrimitiveRestartIndex() without current GL context"; +} + void NoContextGLApi::glProgramBinaryFn(GLuint program, GLenum binaryFormat, const GLvoid* binary, diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.h b/chromium/ui/gl/gl_bindings_autogen_gl.h index 9e0ae429b74..28393dca3f4 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_autogen_gl.h @@ -637,6 +637,7 @@ typedef void(GL_BINDING_CALL* glPointParameteriProc)(GLenum pname, GLint param); typedef void(GL_BINDING_CALL* glPolygonOffsetProc)(GLfloat factor, GLfloat units); typedef void(GL_BINDING_CALL* glPopGroupMarkerEXTProc)(void); +typedef void(GL_BINDING_CALL* glPrimitiveRestartIndexProc)(GLuint index); typedef void(GL_BINDING_CALL* glProgramBinaryProc)(GLuint program, GLenum binaryFormat, const GLvoid* binary, @@ -1075,6 +1076,7 @@ struct ExtensionsGL { bool b_GL_EXT_shader_image_load_store; bool b_GL_EXT_texture_storage; bool b_GL_EXT_timer_query; + bool b_GL_EXT_unpack_subimage; bool b_GL_IMG_multisampled_render_to_texture; bool b_GL_INTEL_framebuffer_CMAA; bool b_GL_KHR_blend_equation_advanced; @@ -1305,6 +1307,7 @@ struct ProcsGL { glPointParameteriProc glPointParameteriFn; glPolygonOffsetProc glPolygonOffsetFn; glPopGroupMarkerEXTProc glPopGroupMarkerEXTFn; + glPrimitiveRestartIndexProc glPrimitiveRestartIndexFn; glProgramBinaryProc glProgramBinaryFn; glProgramParameteriProc glProgramParameteriFn; glProgramPathFragmentInputGenNVProc glProgramPathFragmentInputGenNVFn; @@ -1963,6 +1966,7 @@ class GL_EXPORT GLApi { virtual void glPointParameteriFn(GLenum pname, GLint param) = 0; virtual void glPolygonOffsetFn(GLfloat factor, GLfloat units) = 0; virtual void glPopGroupMarkerEXTFn(void) = 0; + virtual void glPrimitiveRestartIndexFn(GLuint index) = 0; virtual void glProgramBinaryFn(GLuint program, GLenum binaryFormat, const GLvoid* binary, @@ -2600,6 +2604,8 @@ class GL_EXPORT GLApi { #define glPointParameteri ::gfx::g_current_gl_context->glPointParameteriFn #define glPolygonOffset ::gfx::g_current_gl_context->glPolygonOffsetFn #define glPopGroupMarkerEXT ::gfx::g_current_gl_context->glPopGroupMarkerEXTFn +#define glPrimitiveRestartIndex \ + ::gfx::g_current_gl_context->glPrimitiveRestartIndexFn #define glProgramBinary ::gfx::g_current_gl_context->glProgramBinaryFn #define glProgramParameteri ::gfx::g_current_gl_context->glProgramParameteriFn #define glProgramPathFragmentInputGenNV \ diff --git a/chromium/ui/gl/gl_bindings_autogen_glx.cc b/chromium/ui/gl/gl_bindings_autogen_glx.cc index 02aee398e4f..819cd0149d4 100644 --- a/chromium/ui/gl/gl_bindings_autogen_glx.cc +++ b/chromium/ui/gl/gl_bindings_autogen_glx.cc @@ -212,6 +212,7 @@ static void GL_BINDING_CALL Debug_glXBindTexImageEXT(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << buffer << ", " << static_cast<const void*>(attribList) << ")"); + DCHECK(g_driver_glx.debug_fn.glXBindTexImageEXTFn != nullptr); g_driver_glx.debug_fn.glXBindTexImageEXTFn(dpy, drawable, buffer, attribList); } @@ -224,6 +225,7 @@ Debug_glXChooseFBConfig(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << screen << ", " << static_cast<const void*>(attribList) << ", " << static_cast<const void*>(nitems) << ")"); + DCHECK(g_driver_glx.debug_fn.glXChooseFBConfigFn != nullptr); GLXFBConfig* result = g_driver_glx.debug_fn.glXChooseFBConfigFn( dpy, screen, attribList, nitems); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -236,6 +238,7 @@ static XVisualInfo* GL_BINDING_CALL Debug_glXChooseVisual(Display* dpy, GL_SERVICE_LOG("glXChooseVisual" << "(" << static_cast<const void*>(dpy) << ", " << screen << ", " << static_cast<const void*>(attribList) << ")"); + DCHECK(g_driver_glx.debug_fn.glXChooseVisualFn != nullptr); XVisualInfo* result = g_driver_glx.debug_fn.glXChooseVisualFn(dpy, screen, attribList); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -249,6 +252,7 @@ static void GL_BINDING_CALL Debug_glXCopyContext(Display* dpy, GL_SERVICE_LOG("glXCopyContext" << "(" << static_cast<const void*>(dpy) << ", " << src << ", " << dst << ", " << mask << ")"); + DCHECK(g_driver_glx.debug_fn.glXCopyContextFn != nullptr); g_driver_glx.debug_fn.glXCopyContextFn(dpy, src, dst, mask); } @@ -262,6 +266,7 @@ static void GL_BINDING_CALL Debug_glXCopySubBufferMESA(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << x << ", " << y << ", " << width << ", " << height << ")"); + DCHECK(g_driver_glx.debug_fn.glXCopySubBufferMESAFn != nullptr); g_driver_glx.debug_fn.glXCopySubBufferMESAFn(dpy, drawable, x, y, width, height); } @@ -274,6 +279,7 @@ static GLXContext GL_BINDING_CALL Debug_glXCreateContext(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << static_cast<const void*>(vis) << ", " << shareList << ", " << direct << ")"); + DCHECK(g_driver_glx.debug_fn.glXCreateContextFn != nullptr); GLXContext result = g_driver_glx.debug_fn.glXCreateContextFn(dpy, vis, shareList, direct); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -290,6 +296,7 @@ Debug_glXCreateContextAttribsARB(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << config << ", " << share_context << ", " << direct << ", " << static_cast<const void*>(attrib_list) << ")"); + DCHECK(g_driver_glx.debug_fn.glXCreateContextAttribsARBFn != nullptr); GLXContext result = g_driver_glx.debug_fn.glXCreateContextAttribsARBFn( dpy, config, share_context, direct, attrib_list); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -302,6 +309,7 @@ static GLXPixmap GL_BINDING_CALL Debug_glXCreateGLXPixmap(Display* dpy, GL_SERVICE_LOG("glXCreateGLXPixmap" << "(" << static_cast<const void*>(dpy) << ", " << static_cast<const void*>(visual) << ", " << pixmap << ")"); + DCHECK(g_driver_glx.debug_fn.glXCreateGLXPixmapFn != nullptr); GLXPixmap result = g_driver_glx.debug_fn.glXCreateGLXPixmapFn(dpy, visual, pixmap); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -318,6 +326,7 @@ Debug_glXCreateNewContext(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << config << ", " << renderType << ", " << shareList << ", " << direct << ")"); + DCHECK(g_driver_glx.debug_fn.glXCreateNewContextFn != nullptr); GLXContext result = g_driver_glx.debug_fn.glXCreateNewContextFn( dpy, config, renderType, shareList, direct); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -331,6 +340,7 @@ Debug_glXCreatePbuffer(Display* dpy, GL_SERVICE_LOG("glXCreatePbuffer" << "(" << static_cast<const void*>(dpy) << ", " << config << ", " << static_cast<const void*>(attribList) << ")"); + DCHECK(g_driver_glx.debug_fn.glXCreatePbufferFn != nullptr); GLXPbuffer result = g_driver_glx.debug_fn.glXCreatePbufferFn(dpy, config, attribList); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -345,6 +355,7 @@ static GLXPixmap GL_BINDING_CALL Debug_glXCreatePixmap(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << config << ", " << pixmap << ", " << static_cast<const void*>(attribList) << ")"); + DCHECK(g_driver_glx.debug_fn.glXCreatePixmapFn != nullptr); GLXPixmap result = g_driver_glx.debug_fn.glXCreatePixmapFn(dpy, config, pixmap, attribList); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -359,6 +370,7 @@ static GLXWindow GL_BINDING_CALL Debug_glXCreateWindow(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << config << ", " << win << ", " << static_cast<const void*>(attribList) << ")"); + DCHECK(g_driver_glx.debug_fn.glXCreateWindowFn != nullptr); GLXWindow result = g_driver_glx.debug_fn.glXCreateWindowFn(dpy, config, win, attribList); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -369,6 +381,7 @@ static void GL_BINDING_CALL Debug_glXDestroyContext(Display* dpy, GLXContext ctx) { GL_SERVICE_LOG("glXDestroyContext" << "(" << static_cast<const void*>(dpy) << ", " << ctx << ")"); + DCHECK(g_driver_glx.debug_fn.glXDestroyContextFn != nullptr); g_driver_glx.debug_fn.glXDestroyContextFn(dpy, ctx); } @@ -377,6 +390,7 @@ static void GL_BINDING_CALL Debug_glXDestroyGLXPixmap(Display* dpy, GL_SERVICE_LOG("glXDestroyGLXPixmap" << "(" << static_cast<const void*>(dpy) << ", " << pixmap << ")"); + DCHECK(g_driver_glx.debug_fn.glXDestroyGLXPixmapFn != nullptr); g_driver_glx.debug_fn.glXDestroyGLXPixmapFn(dpy, pixmap); } @@ -385,6 +399,7 @@ static void GL_BINDING_CALL Debug_glXDestroyPbuffer(Display* dpy, GL_SERVICE_LOG("glXDestroyPbuffer" << "(" << static_cast<const void*>(dpy) << ", " << pbuf << ")"); + DCHECK(g_driver_glx.debug_fn.glXDestroyPbufferFn != nullptr); g_driver_glx.debug_fn.glXDestroyPbufferFn(dpy, pbuf); } @@ -393,6 +408,7 @@ static void GL_BINDING_CALL Debug_glXDestroyPixmap(Display* dpy, GL_SERVICE_LOG("glXDestroyPixmap" << "(" << static_cast<const void*>(dpy) << ", " << pixmap << ")"); + DCHECK(g_driver_glx.debug_fn.glXDestroyPixmapFn != nullptr); g_driver_glx.debug_fn.glXDestroyPixmapFn(dpy, pixmap); } @@ -401,6 +417,7 @@ static void GL_BINDING_CALL Debug_glXDestroyWindow(Display* dpy, GL_SERVICE_LOG("glXDestroyWindow" << "(" << static_cast<const void*>(dpy) << ", " << window << ")"); + DCHECK(g_driver_glx.debug_fn.glXDestroyWindowFn != nullptr); g_driver_glx.debug_fn.glXDestroyWindowFn(dpy, window); } @@ -409,6 +426,7 @@ static const char* GL_BINDING_CALL Debug_glXGetClientString(Display* dpy, GL_SERVICE_LOG("glXGetClientString" << "(" << static_cast<const void*>(dpy) << ", " << name << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetClientStringFn != nullptr); const char* result = g_driver_glx.debug_fn.glXGetClientStringFn(dpy, name); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -422,6 +440,7 @@ static int GL_BINDING_CALL Debug_glXGetConfig(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << static_cast<const void*>(visual) << ", " << attrib << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetConfigFn != nullptr); int result = g_driver_glx.debug_fn.glXGetConfigFn(dpy, visual, attrib, value); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -431,6 +450,7 @@ static GLXContext GL_BINDING_CALL Debug_glXGetCurrentContext(void) { GL_SERVICE_LOG("glXGetCurrentContext" << "(" << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetCurrentContextFn != nullptr); GLXContext result = g_driver_glx.debug_fn.glXGetCurrentContextFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -440,6 +460,7 @@ static Display* GL_BINDING_CALL Debug_glXGetCurrentDisplay(void) { GL_SERVICE_LOG("glXGetCurrentDisplay" << "(" << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetCurrentDisplayFn != nullptr); Display* result = g_driver_glx.debug_fn.glXGetCurrentDisplayFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -449,6 +470,7 @@ static GLXDrawable GL_BINDING_CALL Debug_glXGetCurrentDrawable(void) { GL_SERVICE_LOG("glXGetCurrentDrawable" << "(" << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetCurrentDrawableFn != nullptr); GLXDrawable result = g_driver_glx.debug_fn.glXGetCurrentDrawableFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -458,6 +480,7 @@ static GLXDrawable GL_BINDING_CALL Debug_glXGetCurrentReadDrawable(void) { GL_SERVICE_LOG("glXGetCurrentReadDrawable" << "(" << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetCurrentReadDrawableFn != nullptr); GLXDrawable result = g_driver_glx.debug_fn.glXGetCurrentReadDrawableFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -471,6 +494,7 @@ static int GL_BINDING_CALL Debug_glXGetFBConfigAttrib(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << config << ", " << attribute << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetFBConfigAttribFn != nullptr); int result = g_driver_glx.debug_fn.glXGetFBConfigAttribFn(dpy, config, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -482,6 +506,7 @@ Debug_glXGetFBConfigFromVisualSGIX(Display* dpy, XVisualInfo* visualInfo) { GL_SERVICE_LOG("glXGetFBConfigFromVisualSGIX" << "(" << static_cast<const void*>(dpy) << ", " << static_cast<const void*>(visualInfo) << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetFBConfigFromVisualSGIXFn != nullptr); GLXFBConfig result = g_driver_glx.debug_fn.glXGetFBConfigFromVisualSGIXFn(dpy, visualInfo); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -494,6 +519,7 @@ static GLXFBConfig* GL_BINDING_CALL Debug_glXGetFBConfigs(Display* dpy, GL_SERVICE_LOG("glXGetFBConfigs" << "(" << static_cast<const void*>(dpy) << ", " << screen << ", " << static_cast<const void*>(nelements) << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetFBConfigsFn != nullptr); GLXFBConfig* result = g_driver_glx.debug_fn.glXGetFBConfigsFn(dpy, screen, nelements); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -508,6 +534,7 @@ static bool GL_BINDING_CALL Debug_glXGetMscRateOML(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << static_cast<const void*>(numerator) << ", " << static_cast<const void*>(denominator) << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetMscRateOMLFn != nullptr); bool result = g_driver_glx.debug_fn.glXGetMscRateOMLFn( dpy, drawable, numerator, denominator); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -520,6 +547,7 @@ static void GL_BINDING_CALL Debug_glXGetSelectedEvent(Display* dpy, GL_SERVICE_LOG("glXGetSelectedEvent" << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << static_cast<const void*>(mask) << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetSelectedEventFn != nullptr); g_driver_glx.debug_fn.glXGetSelectedEventFn(dpy, drawable, mask); } @@ -533,6 +561,7 @@ static bool GL_BINDING_CALL Debug_glXGetSyncValuesOML(Display* dpy, << ", " << static_cast<const void*>(ust) << ", " << static_cast<const void*>(msc) << ", " << static_cast<const void*>(sbc) << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetSyncValuesOMLFn != nullptr); bool result = g_driver_glx.debug_fn.glXGetSyncValuesOMLFn(dpy, drawable, ust, msc, sbc); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -544,6 +573,7 @@ Debug_glXGetVisualFromFBConfig(Display* dpy, GLXFBConfig config) { GL_SERVICE_LOG("glXGetVisualFromFBConfig" << "(" << static_cast<const void*>(dpy) << ", " << config << ")"); + DCHECK(g_driver_glx.debug_fn.glXGetVisualFromFBConfigFn != nullptr); XVisualInfo* result = g_driver_glx.debug_fn.glXGetVisualFromFBConfigFn(dpy, config); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -553,6 +583,7 @@ Debug_glXGetVisualFromFBConfig(Display* dpy, GLXFBConfig config) { static int GL_BINDING_CALL Debug_glXIsDirect(Display* dpy, GLXContext ctx) { GL_SERVICE_LOG("glXIsDirect" << "(" << static_cast<const void*>(dpy) << ", " << ctx << ")"); + DCHECK(g_driver_glx.debug_fn.glXIsDirectFn != nullptr); int result = g_driver_glx.debug_fn.glXIsDirectFn(dpy, ctx); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -565,6 +596,7 @@ static int GL_BINDING_CALL Debug_glXMakeContextCurrent(Display* dpy, GL_SERVICE_LOG("glXMakeContextCurrent" << "(" << static_cast<const void*>(dpy) << ", " << draw << ", " << read << ", " << ctx << ")"); + DCHECK(g_driver_glx.debug_fn.glXMakeContextCurrentFn != nullptr); int result = g_driver_glx.debug_fn.glXMakeContextCurrentFn(dpy, draw, read, ctx); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -577,6 +609,7 @@ static int GL_BINDING_CALL Debug_glXMakeCurrent(Display* dpy, GL_SERVICE_LOG("glXMakeCurrent" << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << ctx << ")"); + DCHECK(g_driver_glx.debug_fn.glXMakeCurrentFn != nullptr); int result = g_driver_glx.debug_fn.glXMakeCurrentFn(dpy, drawable, ctx); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -590,6 +623,7 @@ static int GL_BINDING_CALL Debug_glXQueryContext(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << ctx << ", " << attribute << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_glx.debug_fn.glXQueryContextFn != nullptr); int result = g_driver_glx.debug_fn.glXQueryContextFn(dpy, ctx, attribute, value); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -604,6 +638,7 @@ static void GL_BINDING_CALL Debug_glXQueryDrawable(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << draw << ", " << attribute << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_glx.debug_fn.glXQueryDrawableFn != nullptr); g_driver_glx.debug_fn.glXQueryDrawableFn(dpy, draw, attribute, value); } @@ -614,6 +649,7 @@ static int GL_BINDING_CALL Debug_glXQueryExtension(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << static_cast<const void*>(errorb) << ", " << static_cast<const void*>(event) << ")"); + DCHECK(g_driver_glx.debug_fn.glXQueryExtensionFn != nullptr); int result = g_driver_glx.debug_fn.glXQueryExtensionFn(dpy, errorb, event); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -624,6 +660,7 @@ static const char* GL_BINDING_CALL Debug_glXQueryExtensionsString(Display* dpy, GL_SERVICE_LOG("glXQueryExtensionsString" << "(" << static_cast<const void*>(dpy) << ", " << screen << ")"); + DCHECK(g_driver_glx.debug_fn.glXQueryExtensionsStringFn != nullptr); const char* result = g_driver_glx.debug_fn.glXQueryExtensionsStringFn(dpy, screen); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -636,6 +673,7 @@ static const char* GL_BINDING_CALL Debug_glXQueryServerString(Display* dpy, GL_SERVICE_LOG("glXQueryServerString" << "(" << static_cast<const void*>(dpy) << ", " << screen << ", " << name << ")"); + DCHECK(g_driver_glx.debug_fn.glXQueryServerStringFn != nullptr); const char* result = g_driver_glx.debug_fn.glXQueryServerStringFn(dpy, screen, name); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -649,6 +687,7 @@ static int GL_BINDING_CALL Debug_glXQueryVersion(Display* dpy, << "(" << static_cast<const void*>(dpy) << ", " << static_cast<const void*>(maj) << ", " << static_cast<const void*>(min) << ")"); + DCHECK(g_driver_glx.debug_fn.glXQueryVersionFn != nullptr); int result = g_driver_glx.debug_fn.glXQueryVersionFn(dpy, maj, min); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -660,6 +699,7 @@ static void GL_BINDING_CALL Debug_glXReleaseTexImageEXT(Display* dpy, GL_SERVICE_LOG("glXReleaseTexImageEXT" << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << buffer << ")"); + DCHECK(g_driver_glx.debug_fn.glXReleaseTexImageEXTFn != nullptr); g_driver_glx.debug_fn.glXReleaseTexImageEXTFn(dpy, drawable, buffer); } @@ -669,6 +709,7 @@ static void GL_BINDING_CALL Debug_glXSelectEvent(Display* dpy, GL_SERVICE_LOG("glXSelectEvent" << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << mask << ")"); + DCHECK(g_driver_glx.debug_fn.glXSelectEventFn != nullptr); g_driver_glx.debug_fn.glXSelectEventFn(dpy, drawable, mask); } @@ -677,6 +718,7 @@ static void GL_BINDING_CALL Debug_glXSwapBuffers(Display* dpy, GL_SERVICE_LOG("glXSwapBuffers" << "(" << static_cast<const void*>(dpy) << ", " << drawable << ")"); + DCHECK(g_driver_glx.debug_fn.glXSwapBuffersFn != nullptr); g_driver_glx.debug_fn.glXSwapBuffersFn(dpy, drawable); } @@ -686,12 +728,14 @@ static void GL_BINDING_CALL Debug_glXSwapIntervalEXT(Display* dpy, GL_SERVICE_LOG("glXSwapIntervalEXT" << "(" << static_cast<const void*>(dpy) << ", " << drawable << ", " << interval << ")"); + DCHECK(g_driver_glx.debug_fn.glXSwapIntervalEXTFn != nullptr); g_driver_glx.debug_fn.glXSwapIntervalEXTFn(dpy, drawable, interval); } static void GL_BINDING_CALL Debug_glXSwapIntervalMESA(unsigned int interval) { GL_SERVICE_LOG("glXSwapIntervalMESA" << "(" << interval << ")"); + DCHECK(g_driver_glx.debug_fn.glXSwapIntervalMESAFn != nullptr); g_driver_glx.debug_fn.glXSwapIntervalMESAFn(interval); } @@ -702,6 +746,7 @@ static void GL_BINDING_CALL Debug_glXUseXFont(Font font, GL_SERVICE_LOG("glXUseXFont" << "(" << font << ", " << first << ", " << count << ", " << list << ")"); + DCHECK(g_driver_glx.debug_fn.glXUseXFontFn != nullptr); g_driver_glx.debug_fn.glXUseXFontFn(font, first, count, list); } @@ -709,6 +754,7 @@ static void GL_BINDING_CALL Debug_glXWaitGL(void) { GL_SERVICE_LOG("glXWaitGL" << "(" << ")"); + DCHECK(g_driver_glx.debug_fn.glXWaitGLFn != nullptr); g_driver_glx.debug_fn.glXWaitGLFn(); } @@ -718,6 +764,7 @@ static int GL_BINDING_CALL Debug_glXWaitVideoSyncSGI(int divisor, GL_SERVICE_LOG("glXWaitVideoSyncSGI" << "(" << divisor << ", " << remainder << ", " << static_cast<const void*>(count) << ")"); + DCHECK(g_driver_glx.debug_fn.glXWaitVideoSyncSGIFn != nullptr); int result = g_driver_glx.debug_fn.glXWaitVideoSyncSGIFn(divisor, remainder, count); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -728,6 +775,7 @@ static void GL_BINDING_CALL Debug_glXWaitX(void) { GL_SERVICE_LOG("glXWaitX" << "(" << ")"); + DCHECK(g_driver_glx.debug_fn.glXWaitXFn != nullptr); g_driver_glx.debug_fn.glXWaitXFn(); } } // extern "C" diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.cc b/chromium/ui/gl/gl_bindings_autogen_mock.cc index 2bb8516fc30..d813760c01d 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.cc +++ b/chromium/ui/gl/gl_bindings_autogen_mock.cc @@ -1986,6 +1986,12 @@ void GL_BINDING_CALL MockGLInterface::Mock_glPopGroupMarkerEXT(void) { interface_->PopGroupMarkerEXT(); } +void GL_BINDING_CALL +MockGLInterface::Mock_glPrimitiveRestartIndex(GLuint index) { + MakeFunctionUnique("glPrimitiveRestartIndex"); + interface_->PrimitiveRestartIndex(index); +} + void GL_BINDING_CALL MockGLInterface::Mock_glProgramBinary(GLuint program, GLenum binaryFormat, const GLvoid* binary, @@ -3442,6 +3448,8 @@ void* GL_BINDING_CALL MockGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<void*>(Mock_glPolygonOffset); if (strcmp(name, "glPopGroupMarkerEXT") == 0) return reinterpret_cast<void*>(Mock_glPopGroupMarkerEXT); + if (strcmp(name, "glPrimitiveRestartIndex") == 0) + return reinterpret_cast<void*>(Mock_glPrimitiveRestartIndex); if (strcmp(name, "glProgramBinary") == 0) return reinterpret_cast<void*>(Mock_glProgramBinary); if (strcmp(name, "glProgramBinaryOES") == 0) diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.h b/chromium/ui/gl/gl_bindings_autogen_mock.h index 283d40ca8eb..88a06fef5d1 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.h +++ b/chromium/ui/gl/gl_bindings_autogen_mock.h @@ -745,6 +745,7 @@ 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_glPolygonOffset(GLfloat factor, GLfloat units); static void GL_BINDING_CALL Mock_glPopGroupMarkerEXT(void); +static void GL_BINDING_CALL Mock_glPrimitiveRestartIndex(GLuint index); static void GL_BINDING_CALL Mock_glProgramBinary(GLuint program, GLenum binaryFormat, const GLvoid* binary, diff --git a/chromium/ui/gl/gl_bindings_autogen_osmesa.cc b/chromium/ui/gl/gl_bindings_autogen_osmesa.cc index 27d31e27584..1f03fe33082 100644 --- a/chromium/ui/gl/gl_bindings_autogen_osmesa.cc +++ b/chromium/ui/gl/gl_bindings_autogen_osmesa.cc @@ -62,6 +62,7 @@ extern "C" { static void GL_BINDING_CALL Debug_OSMesaColorClamp(GLboolean enable) { GL_SERVICE_LOG("OSMesaColorClamp" << "(" << GLEnums::GetStringBool(enable) << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaColorClampFn != nullptr); g_driver_osmesa.debug_fn.OSMesaColorClampFn(enable); } @@ -70,6 +71,7 @@ Debug_OSMesaCreateContext(GLenum format, OSMesaContext sharelist) { GL_SERVICE_LOG("OSMesaCreateContext" << "(" << GLEnums::GetStringEnum(format) << ", " << sharelist << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaCreateContextFn != nullptr); OSMesaContext result = g_driver_osmesa.debug_fn.OSMesaCreateContextFn(format, sharelist); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -86,6 +88,7 @@ Debug_OSMesaCreateContextExt(GLenum format, << "(" << GLEnums::GetStringEnum(format) << ", " << depthBits << ", " << stencilBits << ", " << accumBits << ", " << sharelist << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaCreateContextExtFn != nullptr); OSMesaContext result = g_driver_osmesa.debug_fn.OSMesaCreateContextExtFn( format, depthBits, stencilBits, accumBits, sharelist); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -95,6 +98,7 @@ Debug_OSMesaCreateContextExt(GLenum format, static void GL_BINDING_CALL Debug_OSMesaDestroyContext(OSMesaContext ctx) { GL_SERVICE_LOG("OSMesaDestroyContext" << "(" << ctx << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaDestroyContextFn != nullptr); g_driver_osmesa.debug_fn.OSMesaDestroyContextFn(ctx); } @@ -107,6 +111,7 @@ static GLboolean GL_BINDING_CALL Debug_OSMesaGetColorBuffer(OSMesaContext c, << "(" << c << ", " << static_cast<const void*>(width) << ", " << static_cast<const void*>(height) << ", " << static_cast<const void*>(format) << ", " << buffer << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaGetColorBufferFn != nullptr); GLboolean result = g_driver_osmesa.debug_fn.OSMesaGetColorBufferFn( c, width, height, format, buffer); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -117,6 +122,7 @@ static OSMesaContext GL_BINDING_CALL Debug_OSMesaGetCurrentContext(void) { GL_SERVICE_LOG("OSMesaGetCurrentContext" << "(" << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaGetCurrentContextFn != nullptr); OSMesaContext result = g_driver_osmesa.debug_fn.OSMesaGetCurrentContextFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -133,6 +139,7 @@ Debug_OSMesaGetDepthBuffer(OSMesaContext c, << static_cast<const void*>(height) << ", " << static_cast<const void*>(bytesPerValue) << ", " << buffer << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaGetDepthBufferFn != nullptr); GLboolean result = g_driver_osmesa.debug_fn.OSMesaGetDepthBufferFn( c, width, height, bytesPerValue, buffer); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -143,6 +150,7 @@ static void GL_BINDING_CALL Debug_OSMesaGetIntegerv(GLint pname, GLint* value) { GL_SERVICE_LOG("OSMesaGetIntegerv" << "(" << pname << ", " << static_cast<const void*>(value) << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaGetIntegervFn != nullptr); g_driver_osmesa.debug_fn.OSMesaGetIntegervFn(pname, value); } @@ -150,6 +158,7 @@ static OSMESAproc GL_BINDING_CALL Debug_OSMesaGetProcAddress(const char* funcName) { GL_SERVICE_LOG("OSMesaGetProcAddress" << "(" << funcName << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaGetProcAddressFn != nullptr); OSMESAproc result = g_driver_osmesa.debug_fn.OSMesaGetProcAddressFn(funcName); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -164,6 +173,7 @@ static GLboolean GL_BINDING_CALL Debug_OSMesaMakeCurrent(OSMesaContext ctx, << "(" << ctx << ", " << static_cast<const void*>(buffer) << ", " << GLEnums::GetStringEnum(type) << ", " << width << ", " << height << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaMakeCurrentFn != nullptr); GLboolean result = g_driver_osmesa.debug_fn.OSMesaMakeCurrentFn( ctx, buffer, type, width, height); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -173,6 +183,7 @@ static GLboolean GL_BINDING_CALL Debug_OSMesaMakeCurrent(OSMesaContext ctx, static void GL_BINDING_CALL Debug_OSMesaPixelStore(GLint pname, GLint value) { GL_SERVICE_LOG("OSMesaPixelStore" << "(" << pname << ", " << value << ")"); + DCHECK(g_driver_osmesa.debug_fn.OSMesaPixelStoreFn != nullptr); g_driver_osmesa.debug_fn.OSMesaPixelStoreFn(pname, value); } } // extern "C" diff --git a/chromium/ui/gl/gl_bindings_autogen_wgl.cc b/chromium/ui/gl/gl_bindings_autogen_wgl.cc index f1bcb2f2ae5..4791424e7dc 100644 --- a/chromium/ui/gl/gl_bindings_autogen_wgl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_wgl.cc @@ -135,6 +135,7 @@ Debug_wglChoosePixelFormatARB(HDC dc, << static_cast<const void*>(float_attrib_list) << ", " << max_formats << ", " << static_cast<const void*>(formats) << ", " << static_cast<const void*>(num_formats) << ")"); + DCHECK(g_driver_wgl.debug_fn.wglChoosePixelFormatARBFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglChoosePixelFormatARBFn( dc, int_attrib_list, float_attrib_list, max_formats, formats, num_formats); @@ -147,6 +148,7 @@ static BOOL GL_BINDING_CALL Debug_wglCopyContext(HGLRC hglrcSrc, UINT mask) { GL_SERVICE_LOG("wglCopyContext" << "(" << hglrcSrc << ", " << hglrcDst << ", " << mask << ")"); + DCHECK(g_driver_wgl.debug_fn.wglCopyContextFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglCopyContextFn(hglrcSrc, hglrcDst, mask); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -156,6 +158,7 @@ static BOOL GL_BINDING_CALL Debug_wglCopyContext(HGLRC hglrcSrc, static HGLRC GL_BINDING_CALL Debug_wglCreateContext(HDC hdc) { GL_SERVICE_LOG("wglCreateContext" << "(" << hdc << ")"); + DCHECK(g_driver_wgl.debug_fn.wglCreateContextFn != nullptr); HGLRC result = g_driver_wgl.debug_fn.wglCreateContextFn(hdc); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -165,6 +168,7 @@ static HGLRC GL_BINDING_CALL Debug_wglCreateLayerContext(HDC hdc, int iLayerPlane) { GL_SERVICE_LOG("wglCreateLayerContext" << "(" << hdc << ", " << iLayerPlane << ")"); + DCHECK(g_driver_wgl.debug_fn.wglCreateLayerContextFn != nullptr); HGLRC result = g_driver_wgl.debug_fn.wglCreateLayerContextFn(hdc, iLayerPlane); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -181,6 +185,7 @@ Debug_wglCreatePbufferARB(HDC hDC, << "(" << hDC << ", " << iPixelFormat << ", " << iWidth << ", " << iHeight << ", " << static_cast<const void*>(piAttribList) << ")"); + DCHECK(g_driver_wgl.debug_fn.wglCreatePbufferARBFn != nullptr); HPBUFFERARB result = g_driver_wgl.debug_fn.wglCreatePbufferARBFn( hDC, iPixelFormat, iWidth, iHeight, piAttribList); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -190,6 +195,7 @@ Debug_wglCreatePbufferARB(HDC hDC, static BOOL GL_BINDING_CALL Debug_wglDeleteContext(HGLRC hglrc) { GL_SERVICE_LOG("wglDeleteContext" << "(" << hglrc << ")"); + DCHECK(g_driver_wgl.debug_fn.wglDeleteContextFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglDeleteContextFn(hglrc); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -198,6 +204,7 @@ static BOOL GL_BINDING_CALL Debug_wglDeleteContext(HGLRC hglrc) { static BOOL GL_BINDING_CALL Debug_wglDestroyPbufferARB(HPBUFFERARB hPbuffer) { GL_SERVICE_LOG("wglDestroyPbufferARB" << "(" << hPbuffer << ")"); + DCHECK(g_driver_wgl.debug_fn.wglDestroyPbufferARBFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglDestroyPbufferARBFn(hPbuffer); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -207,6 +214,7 @@ static HGLRC GL_BINDING_CALL Debug_wglGetCurrentContext() { GL_SERVICE_LOG("wglGetCurrentContext" << "(" << ")"); + DCHECK(g_driver_wgl.debug_fn.wglGetCurrentContextFn != nullptr); HGLRC result = g_driver_wgl.debug_fn.wglGetCurrentContextFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -216,6 +224,7 @@ static HDC GL_BINDING_CALL Debug_wglGetCurrentDC() { GL_SERVICE_LOG("wglGetCurrentDC" << "(" << ")"); + DCHECK(g_driver_wgl.debug_fn.wglGetCurrentDCFn != nullptr); HDC result = g_driver_wgl.debug_fn.wglGetCurrentDCFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -224,6 +233,7 @@ static HDC GL_BINDING_CALL Debug_wglGetCurrentDC() { static const char* GL_BINDING_CALL Debug_wglGetExtensionsStringARB(HDC hDC) { GL_SERVICE_LOG("wglGetExtensionsStringARB" << "(" << hDC << ")"); + DCHECK(g_driver_wgl.debug_fn.wglGetExtensionsStringARBFn != nullptr); const char* result = g_driver_wgl.debug_fn.wglGetExtensionsStringARBFn(hDC); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -233,6 +243,7 @@ static const char* GL_BINDING_CALL Debug_wglGetExtensionsStringEXT() { GL_SERVICE_LOG("wglGetExtensionsStringEXT" << "(" << ")"); + DCHECK(g_driver_wgl.debug_fn.wglGetExtensionsStringEXTFn != nullptr); const char* result = g_driver_wgl.debug_fn.wglGetExtensionsStringEXTFn(); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -241,6 +252,7 @@ static const char* GL_BINDING_CALL Debug_wglGetExtensionsStringEXT() { static HDC GL_BINDING_CALL Debug_wglGetPbufferDCARB(HPBUFFERARB hPbuffer) { GL_SERVICE_LOG("wglGetPbufferDCARB" << "(" << hPbuffer << ")"); + DCHECK(g_driver_wgl.debug_fn.wglGetPbufferDCARBFn != nullptr); HDC result = g_driver_wgl.debug_fn.wglGetPbufferDCARBFn(hPbuffer); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -249,6 +261,7 @@ static HDC GL_BINDING_CALL Debug_wglGetPbufferDCARB(HPBUFFERARB hPbuffer) { static BOOL GL_BINDING_CALL Debug_wglMakeCurrent(HDC hdc, HGLRC hglrc) { GL_SERVICE_LOG("wglMakeCurrent" << "(" << hdc << ", " << hglrc << ")"); + DCHECK(g_driver_wgl.debug_fn.wglMakeCurrentFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglMakeCurrentFn(hdc, hglrc); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -260,6 +273,7 @@ static BOOL GL_BINDING_CALL Debug_wglQueryPbufferARB(HPBUFFERARB hPbuffer, GL_SERVICE_LOG("wglQueryPbufferARB" << "(" << hPbuffer << ", " << iAttribute << ", " << static_cast<const void*>(piValue) << ")"); + DCHECK(g_driver_wgl.debug_fn.wglQueryPbufferARBFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglQueryPbufferARBFn(hPbuffer, iAttribute, piValue); GL_SERVICE_LOG("GL_RESULT: " << result); @@ -270,6 +284,7 @@ static int GL_BINDING_CALL Debug_wglReleasePbufferDCARB(HPBUFFERARB hPbuffer, HDC hDC) { GL_SERVICE_LOG("wglReleasePbufferDCARB" << "(" << hPbuffer << ", " << hDC << ")"); + DCHECK(g_driver_wgl.debug_fn.wglReleasePbufferDCARBFn != nullptr); int result = g_driver_wgl.debug_fn.wglReleasePbufferDCARBFn(hPbuffer, hDC); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -278,6 +293,7 @@ static int GL_BINDING_CALL Debug_wglReleasePbufferDCARB(HPBUFFERARB hPbuffer, static BOOL GL_BINDING_CALL Debug_wglShareLists(HGLRC hglrc1, HGLRC hglrc2) { GL_SERVICE_LOG("wglShareLists" << "(" << hglrc1 << ", " << hglrc2 << ")"); + DCHECK(g_driver_wgl.debug_fn.wglShareListsFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglShareListsFn(hglrc1, hglrc2); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -286,6 +302,7 @@ static BOOL GL_BINDING_CALL Debug_wglShareLists(HGLRC hglrc1, HGLRC hglrc2) { static BOOL GL_BINDING_CALL Debug_wglSwapIntervalEXT(int interval) { GL_SERVICE_LOG("wglSwapIntervalEXT" << "(" << interval << ")"); + DCHECK(g_driver_wgl.debug_fn.wglSwapIntervalEXTFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglSwapIntervalEXTFn(interval); GL_SERVICE_LOG("GL_RESULT: " << result); return result; @@ -294,6 +311,7 @@ static BOOL GL_BINDING_CALL Debug_wglSwapIntervalEXT(int interval) { static BOOL GL_BINDING_CALL Debug_wglSwapLayerBuffers(HDC hdc, UINT fuPlanes) { GL_SERVICE_LOG("wglSwapLayerBuffers" << "(" << hdc << ", " << fuPlanes << ")"); + DCHECK(g_driver_wgl.debug_fn.wglSwapLayerBuffersFn != nullptr); BOOL result = g_driver_wgl.debug_fn.wglSwapLayerBuffersFn(hdc, fuPlanes); GL_SERVICE_LOG("GL_RESULT: " << result); return result; diff --git a/chromium/ui/gl/gl_bindings_skia_in_process.cc b/chromium/ui/gl/gl_bindings_skia_in_process.cc deleted file mode 100644 index d76bd7aba16..00000000000 --- a/chromium/ui/gl/gl_bindings_skia_in_process.cc +++ /dev/null @@ -1,995 +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/gl/gl_bindings_skia_in_process.h" - -#include "base/logging.h" -#include "third_party/skia/include/gpu/gl/GrGLInterface.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_implementation.h" - -namespace { - -extern "C" { -// The following stub functions are required because the glXXX routines exported -// via gl_bindings.h use call-type GL_BINDING_CALL, which on Windows is stdcall. -// Skia has been built such that its GrGLInterface GL pointers are __cdecl. - -GLvoid GR_GL_FUNCTION_TYPE StubGLActiveTexture(GLenum texture) { - glActiveTexture(texture); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLAttachShader(GLuint program, GLuint shader) { - glAttachShader(program, shader); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBeginQuery(GLenum target, GLuint id) { - glBeginQuery(target, id); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBindAttribLocation(GLuint program, - GLuint index, - const char* name) { - glBindAttribLocation(program, index, name); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBindBuffer(GLenum target, GLuint buffer) { - glBindBuffer(target, buffer); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBindFragDataLocation(GLuint program, - GLuint colorNumber, - const GLchar* name) { - glBindFragDataLocation(program, colorNumber, name); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLBindFragDataLocationIndexed(GLuint program, - GLuint colorNumber, - GLuint index, - const GLchar* name) { - glBindFragDataLocationIndexed(program, colorNumber, index, name); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBindFramebuffer(GLenum target, - GLuint framebuffer) { - glBindFramebufferEXT(target, framebuffer); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBindRenderbuffer(GLenum target, - GLuint renderbuffer) { - glBindRenderbufferEXT(target, renderbuffer); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBindTexture(GLenum target, GLuint texture) { - glBindTexture(target, texture); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBindVertexArray(GLuint array) { - glBindVertexArrayOES(array); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBlendBarrier() { - glBlendBarrierKHR(); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBlendColor(GLclampf red, - GLclampf green, - GLclampf blue, - GLclampf alpha) { - glBlendColor(red, green, blue, alpha); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBlendEquation(GLenum mode) { - glBlendEquation(mode); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBlendFunc(GLenum sfactor, GLenum dfactor) { - glBlendFunc(sfactor, dfactor); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBlitFramebuffer(GLint srcX0, - GLint srcY0, - GLint srcX1, - GLint srcY1, - GLint dstX0, - GLint dstY0, - GLint dstX1, - GLint dstY1, - GLbitfield mask, - GLenum filter) { - glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, - mask, filter); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBufferData(GLenum target, - GLsizeiptr size, - const void* data, - GLenum usage) { - glBufferData(target, size, data, usage); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLBufferSubData(GLenum target, - GLintptr offset, - GLsizeiptr size, - const void* data) { - glBufferSubData(target, offset, size, data); -} - -GLenum GR_GL_FUNCTION_TYPE StubGLCheckFramebufferStatus(GLenum target) { - return glCheckFramebufferStatusEXT(target); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLClear(GLbitfield mask) { - glClear(mask); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLClearColor(GLclampf red, - GLclampf green, - GLclampf blue, - GLclampf alpha) { - glClearColor(red, green, blue, alpha); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLClearStencil(GLint s) { - glClearStencil(s); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLColorMask(GLboolean red, - GLboolean green, - GLboolean blue, - GLboolean alpha) { - glColorMask(red, green, blue, alpha); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLCompileShader(GLuint shader) { - glCompileShader(shader); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLCompressedTexImage2D(GLenum target, - GLint level, - GLenum internalformat, - GLsizei width, - GLsizei height, - GLint border, - GLsizei imageSize, - const void* data) { - glCompressedTexImage2D(target, level, internalformat, width, height, border, - imageSize, data); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLCopyTexSubImage2D(GLenum target, - GLint level, - GLint xoffset, - GLint yoffset, - GLint x, - GLint y, - GLsizei width, - GLsizei height) { - glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); -} - -GLuint GR_GL_FUNCTION_TYPE StubGLCreateProgram(void) { - return glCreateProgram(); -} - -GLuint GR_GL_FUNCTION_TYPE StubGLCreateShader(GLenum type) { - return glCreateShader(type); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLCullFace(GLenum mode) { - glCullFace(mode); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDeleteBuffers(GLsizei n, - const GLuint* buffers) { - glDeleteBuffersARB(n, buffers); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) { - glDeleteFramebuffersEXT(n, framebuffers); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDeleteQueries(GLsizei n, const GLuint* ids) { - glDeleteQueries(n, ids); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDeleteProgram(GLuint program) { - glDeleteProgram(program); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) { - glDeleteRenderbuffersEXT(n, renderbuffers); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDeleteShader(GLuint shader) { - glDeleteShader(shader); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDeleteTextures(GLsizei n, - const GLuint* textures) { - glDeleteTextures(n, textures); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDeleteVertexArrays(GLsizei n, - const GLuint* arrays) { - glDeleteVertexArraysOES(n, arrays); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDepthMask(GLboolean flag) { - glDepthMask(flag); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDisable(GLenum cap) { - glDisable(cap); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDisableVertexAttribArray(GLuint index) { - glDisableVertexAttribArray(index); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDiscardFramebuffer(GLenum target, - GLsizei numAttachments, - const GLenum* attachments) { - glDiscardFramebufferEXT(target, numAttachments, attachments); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDrawArrays(GLenum mode, - GLint first, - GLsizei count) { - glDrawArrays(mode, first, count); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDrawBuffer(GLenum mode) { - glDrawBuffer(mode); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDrawBuffers(GLsizei n, const GLenum* bufs) { - glDrawBuffersARB(n, bufs); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLDrawElements(GLenum mode, - GLsizei count, - GLenum type, - const void* indices) { - glDrawElements(mode, count, type, indices); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLEnable(GLenum cap) { - glEnable(cap); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLEnableVertexAttribArray(GLuint index) { - glEnableVertexAttribArray(index); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLEndQuery(GLenum target) { - glEndQuery(target); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLFinish() { - glFinish(); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLFlush() { - glFlush(); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLFlushMappedBufferRange(GLenum target, - GLintptr offset, - GLsizeiptr length) { - glFlushMappedBufferRange(target, offset, length); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLFramebufferRenderbuffer(GLenum target, - GLenum attachment, - GLenum renderbuffertarget, - GLuint renderbuffer) { - glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, - renderbuffer); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLFramebufferTexture2D(GLenum target, - GLenum attachment, - GLenum textarget, - GLuint texture, - GLint level) { - glFramebufferTexture2DEXT(target, attachment, textarget, texture, level); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLFramebufferTexture2DMultisample(GLenum target, - GLenum attachment, - GLenum textarget, - GLuint texture, - GLint level, - GLsizei samples) { - glFramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture, - level, samples); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLFrontFace(GLenum mode) { - glFrontFace(mode); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGenBuffers(GLsizei n, GLuint* buffers) { - glGenBuffersARB(n, buffers); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGenFramebuffers(GLsizei n, - GLuint* framebuffers) { - glGenFramebuffersEXT(n, framebuffers); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGenQueries(GLsizei n, GLuint* ids) { - glGenQueries(n, ids); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGenRenderbuffers(GLsizei n, - GLuint* renderbuffers) { - glGenRenderbuffersEXT(n, renderbuffers); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGenTextures(GLsizei n, GLuint* textures) { - glGenTextures(n, textures); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGenVertexArrays(GLsizei n, GLuint* arrays) { - glGenVertexArraysOES(n, arrays); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGenerateMipmap(GLenum target) { - glGenerateMipmapEXT(target); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetBufferParameteriv(GLenum target, - GLenum pname, - GLint* params) { - glGetBufferParameteriv(target, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLGetFramebufferAttachmentParameteriv(GLenum target, - GLenum attachment, - GLenum pname, - GLint* params) { - glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, params); -} - -GLenum GR_GL_FUNCTION_TYPE StubGLGetError() { - return glGetError(); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetIntegerv(GLenum pname, GLint* params) { - glGetIntegerv(pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetProgramInfoLog(GLuint program, - GLsizei bufsize, - GLsizei* length, - char* infolog) { - glGetProgramInfoLog(program, bufsize, length, infolog); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetProgramiv(GLuint program, - GLenum pname, - GLint* params) { - glGetProgramiv(program, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetRenderbufferParameteriv(GLenum target, - GLenum pname, - GLint* params) { - glGetRenderbufferParameterivEXT(target, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetShaderInfoLog(GLuint shader, - GLsizei bufsize, - GLsizei* length, - char* infolog) { - glGetShaderInfoLog(shader, bufsize, length, infolog); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetShaderiv(GLuint shader, - GLenum pname, - GLint* params) { - glGetShaderiv(shader, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetShaderPrecisionFormat(GLenum shadertype, - GLenum precisiontype, - GLint* range, - GLint* precision) { - glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision); -} - -const GLubyte* GR_GL_FUNCTION_TYPE StubGLGetString(GLenum name) { - return glGetString(name); -} - -const GLubyte* GR_GL_FUNCTION_TYPE StubGLGetStringi(GLenum name, GLuint index) { - return glGetStringi(name, index); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetQueryiv(GLenum target, - GLenum pname, - GLint* params) { - glGetQueryiv(target, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetQueryObjecti64v(GLuint id, - GLenum pname, - GLint64* params) { - glGetQueryObjecti64v(id, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetQueryObjectiv(GLuint id, - GLenum pname, - GLint* params) { - glGetQueryObjectiv(id, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetQueryObjectui64v(GLuint id, - GLenum pname, - GLuint64* params) { - glGetQueryObjectui64v(id, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetQueryObjectuiv(GLuint id, - GLenum pname, - GLuint* params) { - glGetQueryObjectuiv(id, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLGetTexLevelParameteriv(GLenum target, - GLint level, - GLenum pname, - GLint* params) { - glGetTexLevelParameteriv(target, level, pname, params); -} - -GLint GR_GL_FUNCTION_TYPE StubGLGetUniformLocation(GLuint program, - const char* name) { - return glGetUniformLocation(program, name); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLInsertEventMarker(GLsizei length, - const char* marker) { - glInsertEventMarkerEXT(length, marker); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLInvalidateFramebuffer(GLenum target, - GLsizei numAttachments, - const GLenum* attachments) { - glInvalidateFramebuffer(target, numAttachments, attachments); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLInvalidateSubFramebuffer(GLenum target, - GLsizei numAttachments, - const GLenum* attachments, - GLint x, - GLint y, - GLsizei width, - GLsizei height) { - glInvalidateSubFramebuffer(target, numAttachments, attachments, - x, y, width, height); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLLineWidth(GLfloat width) { - glLineWidth(width); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLLinkProgram(GLuint program) { - glLinkProgram(program); -} - -void* GR_GL_FUNCTION_TYPE StubGLMapBuffer(GLenum target, GLenum access) { - return glMapBuffer(target, access); -} - -void* GR_GL_FUNCTION_TYPE StubGLMapBufferRange(GLenum target, - GLintptr offset, - GLsizeiptr length, - GLbitfield access) { - return glMapBufferRange(target, offset, length, access); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLPixelStorei(GLenum pname, GLint param) { - glPixelStorei(pname, param); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLPopGroupMarker() { - glPopGroupMarkerEXT(); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLPushGroupMarker(GLsizei length, - const char* marker) { - glPushGroupMarkerEXT(length, marker); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLQueryCounter(GLuint id, GLenum target) { - glQueryCounter(id, target); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLReadBuffer(GLenum src) { - glReadBuffer(src); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLReadPixels(GLint x, - GLint y, - GLsizei width, - GLsizei height, - GLenum format, - GLenum type, - void* pixels) { - glReadPixels(x, y, width, height, format, type, pixels); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLRenderbufferStorage(GLenum target, - GLenum internalformat, - GLsizei width, - GLsizei height) { - glRenderbufferStorageEXT(target, internalformat, width, height); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLRenderbufferStorageMultisample(GLenum target, - GLsizei samples, - GLenum internalformat, - GLsizei width, - GLsizei height) { - glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, - height); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLScissor(GLint x, - GLint y, - GLsizei width, - GLsizei height) { - glScissor(x, y, width, height); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLShaderSource(GLuint shader, - GLsizei count, - const char* const* str, - const GLint* length) { - glShaderSource(shader, count, str, length); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLStencilFunc(GLenum func, - GLint ref, - GLuint mask) { - glStencilFunc(func, ref, mask); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLStencilFuncSeparate(GLenum face, - GLenum func, - GLint ref, - GLuint mask) { - glStencilFuncSeparate(face, func, ref, mask); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLStencilMask(GLuint mask) { - glStencilMask(mask); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLStencilMaskSeparate(GLenum face, GLuint mask) { - glStencilMaskSeparate(face, mask); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLStencilOp(GLenum fail, - GLenum zfail, - GLenum zpass) { - glStencilOp(fail, zfail, zpass); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLStencilOpSeparate(GLenum face, - GLenum fail, - GLenum zfail, - GLenum zpass) { - glStencilOpSeparate(face, fail, zfail, zpass); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLTexImage2D(GLenum target, - GLint level, - GLint internalformat, - GLsizei width, - GLsizei height, - GLint border, - GLenum format, - GLenum type, - const void* pixels) { - glTexImage2D(target, level, internalformat, width, height, border, format, - type, pixels); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLTexParameteri(GLenum target, - GLenum pname, - GLint param) { - glTexParameteri(target, pname, param); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLTexParameteriv(GLenum target, - GLenum pname, - const GLint* params) { - glTexParameteriv(target, pname, params); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLTexStorage2D(GLenum target, - GLsizei levels, - GLenum internalFormat, - GLsizei width, - GLsizei height) { - glTexStorage2DEXT(target, levels, internalFormat, width, height); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLTexSubImage2D(GLenum target, - GLint level, - GLint xoffset, - GLint yoffset, - GLsizei width, - GLsizei height, - GLenum format, - GLenum type, - const void* pixels) { - glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - pixels); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform1f(GLint location, GLfloat v) { - glUniform1f(location, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform1i(GLint location, GLint v) { - glUniform1i(location, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform1fv(GLint location, - GLsizei count, - const GLfloat* v) { - glUniform1fv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform1iv(GLint location, - GLsizei count, - const GLint* v) { - glUniform1iv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform2f(GLint location, - GLfloat v0, - GLfloat v1) { - glUniform2f(location, v0, v1); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform2i(GLint location, GLint v0, GLint v1) { - glUniform2i(location, v0, v1); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform2fv(GLint location, - GLsizei count, - const GLfloat* v) { - glUniform2fv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform2iv(GLint location, - GLsizei count, - const GLint* v) { - glUniform2iv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform3f(GLint location, - GLfloat v0, - GLfloat v1, - GLfloat v2) { - glUniform3f(location, v0, v1, v2); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform3i(GLint location, - GLint v0, - GLint v1, - GLint v2) { - glUniform3i(location, v0, v1, v2); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform3fv(GLint location, - GLsizei count, - const GLfloat* v) { - glUniform3fv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform3iv(GLint location, - GLsizei count, - const GLint* v) { - glUniform3iv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform4f(GLint location, - GLfloat v0, - GLfloat v1, - GLfloat v2, - GLfloat v3) { - glUniform4f(location, v0, v1, v2, v3); -} - -GLvoid GR_GL_FUNCTION_TYPE -StubGLUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { - glUniform4i(location, v0, v1, v2, v3); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform4fv(GLint location, - GLsizei count, - const GLfloat* v) { - glUniform4fv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniform4iv(GLint location, - GLsizei count, - const GLint* v) { - glUniform4iv(location, count, v); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniformMatrix2fv(GLint location, - GLsizei count, - GLboolean transpose, - const GLfloat* value) { - glUniformMatrix2fv(location, count, transpose, value); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniformMatrix3fv(GLint location, - GLsizei count, - GLboolean transpose, - const GLfloat* value) { - glUniformMatrix3fv(location, count, transpose, value); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUniformMatrix4fv(GLint location, - GLsizei count, - GLboolean transpose, - const GLfloat* value) { - glUniformMatrix4fv(location, count, transpose, value); -} - -GLboolean GR_GL_FUNCTION_TYPE StubGLUnmapBuffer(GLenum target) { - return glUnmapBuffer(target); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLUseProgram(GLuint program) { - glUseProgram(program); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLVertexAttrib1f(GLuint indx, - const GLfloat value) { - glVertexAttrib1f(indx, value); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLVertexAttrib2fv(GLuint indx, - const GLfloat* values) { - glVertexAttrib2fv(indx, values); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLVertexAttrib3fv(GLuint indx, - const GLfloat* values) { - glVertexAttrib3fv(indx, values); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLVertexAttrib4fv(GLuint indx, - const GLfloat* values) { - glVertexAttrib4fv(indx, values); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLVertexAttribPointer(GLuint indx, - GLint size, - GLenum type, - GLboolean normalized, - GLsizei stride, - const void* ptr) { - glVertexAttribPointer(indx, size, type, normalized, stride, ptr); -} - -GLvoid GR_GL_FUNCTION_TYPE StubGLViewport(GLint x, - GLint y, - GLsizei width, - GLsizei height) { - glViewport(x, y, width, height); -} - -GLint GR_GL_FUNCTION_TYPE -StubGLGetProgramResourceLocation(GLuint program, - GLenum programInterface, - const char* name) { - return glGetProgramResourceLocation(program, programInterface, name); -} - -} // extern "C" -} // namespace - -namespace gfx { - -GrGLInterface* CreateInProcessSkiaGLBinding() { - GrGLStandard standard; - switch (gfx::GetGLImplementation()) { - case gfx::kGLImplementationNone: - NOTREACHED(); - return NULL; - case gfx::kGLImplementationDesktopGL: - case gfx::kGLImplementationAppleGL: - standard = kGL_GrGLStandard; - break; - case gfx::kGLImplementationOSMesaGL: - standard = kGL_GrGLStandard; - break; - case gfx::kGLImplementationEGLGLES2: - standard = kGLES_GrGLStandard; - break; - case gfx::kGLImplementationMockGL: - NOTREACHED(); - return NULL; - default: - NOTREACHED(); - return NULL; - } - - GrGLInterface* interface = new GrGLInterface; - interface->fStandard = standard; - interface->fExtensions.init(standard, - StubGLGetString, - StubGLGetStringi, - StubGLGetIntegerv); - - GrGLInterface::Functions* functions = &interface->fFunctions; - functions->fActiveTexture = StubGLActiveTexture; - functions->fAttachShader = StubGLAttachShader; - functions->fBeginQuery = StubGLBeginQuery; - functions->fBindAttribLocation = StubGLBindAttribLocation; - functions->fBindBuffer = StubGLBindBuffer; - functions->fBindFragDataLocation = StubGLBindFragDataLocation; - functions->fBindTexture = StubGLBindTexture; - functions->fBindVertexArray = StubGLBindVertexArray; - functions->fBlendBarrier = StubGLBlendBarrier; - functions->fBlendColor = StubGLBlendColor; - functions->fBlendEquation = StubGLBlendEquation; - functions->fBlendFunc = StubGLBlendFunc; - functions->fBufferData = StubGLBufferData; - functions->fBufferSubData = StubGLBufferSubData; - functions->fClear = StubGLClear; - functions->fClearColor = StubGLClearColor; - functions->fClearStencil = StubGLClearStencil; - functions->fColorMask = StubGLColorMask; - functions->fCompileShader = StubGLCompileShader; - functions->fCompressedTexImage2D = StubGLCompressedTexImage2D; - functions->fCopyTexSubImage2D = StubGLCopyTexSubImage2D; - functions->fCreateProgram = StubGLCreateProgram; - functions->fCreateShader = StubGLCreateShader; - functions->fCullFace = StubGLCullFace; - functions->fDeleteBuffers = StubGLDeleteBuffers; - functions->fDeleteProgram = StubGLDeleteProgram; - functions->fDeleteQueries = StubGLDeleteQueries; - functions->fDeleteShader = StubGLDeleteShader; - functions->fDeleteTextures = StubGLDeleteTextures; - functions->fDeleteVertexArrays = StubGLDeleteVertexArrays; - functions->fDepthMask = StubGLDepthMask; - functions->fDisable = StubGLDisable; - functions->fDisableVertexAttribArray = StubGLDisableVertexAttribArray; - functions->fDiscardFramebuffer = StubGLDiscardFramebuffer; - functions->fDrawArrays = StubGLDrawArrays; - functions->fDrawBuffer = StubGLDrawBuffer; - functions->fDrawBuffers = StubGLDrawBuffers; - functions->fDrawElements = StubGLDrawElements; - functions->fEnable = StubGLEnable; - functions->fEnableVertexAttribArray = StubGLEnableVertexAttribArray; - functions->fEndQuery = StubGLEndQuery; - functions->fFinish = StubGLFinish; - functions->fFlush = StubGLFlush; - functions->fFlushMappedBufferRange = StubGLFlushMappedBufferRange; - functions->fFrontFace = StubGLFrontFace; - functions->fGenBuffers = StubGLGenBuffers; - functions->fGenQueries = StubGLGenQueries; - functions->fGenTextures = StubGLGenTextures; - functions->fGenVertexArrays = StubGLGenVertexArrays; - functions->fGenerateMipmap = StubGLGenerateMipmap; - functions->fGetBufferParameteriv = StubGLGetBufferParameteriv; - functions->fGetError = StubGLGetError; - functions->fGetIntegerv = StubGLGetIntegerv; - functions->fGetQueryiv = StubGLGetQueryiv; - functions->fGetQueryObjecti64v = StubGLGetQueryObjecti64v; - functions->fGetQueryObjectiv = StubGLGetQueryObjectiv; - functions->fGetQueryObjectui64v = StubGLGetQueryObjectui64v; - functions->fGetQueryObjectuiv = StubGLGetQueryObjectuiv; - functions->fGetProgramInfoLog = StubGLGetProgramInfoLog; - functions->fGetProgramiv = StubGLGetProgramiv; - functions->fGetShaderInfoLog = StubGLGetShaderInfoLog; - functions->fGetShaderiv = StubGLGetShaderiv; - functions->fGetShaderPrecisionFormat = StubGLGetShaderPrecisionFormat; - functions->fGetString = StubGLGetString; - functions->fGetStringi = StubGLGetStringi; - functions->fGetTexLevelParameteriv = StubGLGetTexLevelParameteriv; - functions->fGetUniformLocation = StubGLGetUniformLocation; - functions->fInsertEventMarker = StubGLInsertEventMarker; - functions->fInvalidateFramebuffer = StubGLInvalidateFramebuffer; - functions->fInvalidateSubFramebuffer = StubGLInvalidateSubFramebuffer; - functions->fLineWidth = StubGLLineWidth; - functions->fLinkProgram = StubGLLinkProgram; - functions->fMapBufferRange = StubGLMapBufferRange; - functions->fPixelStorei = StubGLPixelStorei; - functions->fPopGroupMarker = StubGLPopGroupMarker; - functions->fPushGroupMarker = StubGLPushGroupMarker; - functions->fQueryCounter = StubGLQueryCounter; - functions->fReadBuffer = StubGLReadBuffer; - functions->fReadPixels = StubGLReadPixels; - functions->fScissor = StubGLScissor; - functions->fShaderSource = StubGLShaderSource; - functions->fStencilFunc = StubGLStencilFunc; - functions->fStencilFuncSeparate = StubGLStencilFuncSeparate; - functions->fStencilMask = StubGLStencilMask; - functions->fStencilMaskSeparate = StubGLStencilMaskSeparate; - functions->fStencilOp = StubGLStencilOp; - functions->fStencilOpSeparate = StubGLStencilOpSeparate; - functions->fTexImage2D = StubGLTexImage2D; - functions->fTexParameteri = StubGLTexParameteri; - functions->fTexParameteriv = StubGLTexParameteriv; - functions->fTexSubImage2D = StubGLTexSubImage2D; - functions->fTexStorage2D = StubGLTexStorage2D; - functions->fUniform1f = StubGLUniform1f; - functions->fUniform1i = StubGLUniform1i; - functions->fUniform1fv = StubGLUniform1fv; - functions->fUniform1iv = StubGLUniform1iv; - functions->fUniform2f = StubGLUniform2f; - functions->fUniform2i = StubGLUniform2i; - functions->fUniform2fv = StubGLUniform2fv; - functions->fUniform2iv = StubGLUniform2iv; - functions->fUniform3f = StubGLUniform3f; - functions->fUniform3i = StubGLUniform3i; - functions->fUniform3fv = StubGLUniform3fv; - functions->fUniform3iv = StubGLUniform3iv; - functions->fUniform4f = StubGLUniform4f; - functions->fUniform4i = StubGLUniform4i; - functions->fUniform4fv = StubGLUniform4fv; - functions->fUniform4iv = StubGLUniform4iv; - functions->fUniformMatrix2fv = StubGLUniformMatrix2fv; - functions->fUniformMatrix3fv = StubGLUniformMatrix3fv; - functions->fUniformMatrix4fv = StubGLUniformMatrix4fv; - functions->fUseProgram = StubGLUseProgram; - functions->fVertexAttrib1f = StubGLVertexAttrib1f; - functions->fVertexAttrib2fv = StubGLVertexAttrib2fv; - functions->fVertexAttrib3fv = StubGLVertexAttrib3fv; - functions->fVertexAttrib4fv = StubGLVertexAttrib4fv; - functions->fVertexAttribPointer = StubGLVertexAttribPointer; - functions->fViewport = StubGLViewport; - functions->fBindFramebuffer = StubGLBindFramebuffer; - functions->fBindRenderbuffer = StubGLBindRenderbuffer; - functions->fCheckFramebufferStatus = StubGLCheckFramebufferStatus; - functions->fDeleteFramebuffers = StubGLDeleteFramebuffers; - functions->fDeleteRenderbuffers = StubGLDeleteRenderbuffers; - functions->fFramebufferRenderbuffer = StubGLFramebufferRenderbuffer; - functions->fFramebufferTexture2D = StubGLFramebufferTexture2D; - functions->fFramebufferTexture2DMultisample = - StubGLFramebufferTexture2DMultisample; - functions->fGenFramebuffers = StubGLGenFramebuffers; - functions->fGenRenderbuffers = StubGLGenRenderbuffers; - functions->fGetFramebufferAttachmentParameteriv = - StubGLGetFramebufferAttachmentParameteriv; - functions->fGetRenderbufferParameteriv = StubGLGetRenderbufferParameteriv; - functions->fRenderbufferStorage = StubGLRenderbufferStorage; - functions->fRenderbufferStorageMultisample = - StubGLRenderbufferStorageMultisample; - functions->fRenderbufferStorageMultisampleES2EXT = - StubGLRenderbufferStorageMultisample; - functions->fBlitFramebuffer = StubGLBlitFramebuffer; - functions->fMapBuffer = StubGLMapBuffer; - functions->fUnmapBuffer = StubGLUnmapBuffer; - functions->fBindFragDataLocationIndexed = - StubGLBindFragDataLocationIndexed; - functions->fGetProgramResourceLocation = StubGLGetProgramResourceLocation; - - return interface; -} - -} // namespace gfx diff --git a/chromium/ui/gl/gl_bindings_skia_in_process.h b/chromium/ui/gl/gl_bindings_skia_in_process.h deleted file mode 100644 index 23d54eecc22..00000000000 --- a/chromium/ui/gl/gl_bindings_skia_in_process.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GL_GL_BINDINGS_SKIA_IN_PROCESS_H_ -#define UI_GL_GL_BINDINGS_SKIA_IN_PROCESS_H_ - -#include "ui/gl/gl_export.h" - -struct GrGLInterface; - -namespace gfx { - -// The GPU back-end for skia requires pointers to GL functions. This function -// creates a binding for skia-gpu to the in-process GL -GL_EXPORT GrGLInterface* CreateInProcessSkiaGLBinding(); - -} // namespace gfx - -#endif // UI_GL_GL_BINDINGS_SKIA_IN_PROCESS_H_ diff --git a/chromium/ui/gl/gl_context_egl.cc b/chromium/ui/gl/gl_context_egl.cc index 8a9e4e4cb2f..569c2cca118 100644 --- a/chromium/ui/gl/gl_context_egl.cc +++ b/chromium/ui/gl/gl_context_egl.cc @@ -70,6 +70,13 @@ bool GLContextEGL::Initialize( context_attributes = kContextAttributes; } + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + LOG(ERROR) << "eglBindApi failed with error " + << GetLastEGLErrorString(); + return false; + } + + context_ = eglCreateContext( display_, config_, diff --git a/chromium/ui/gl/gl_context_osmesa.cc b/chromium/ui/gl/gl_context_osmesa.cc index ee0f7570ebc..dd04b232278 100644 --- a/chromium/ui/gl/gl_context_osmesa.cc +++ b/chromium/ui/gl/gl_context_osmesa.cc @@ -26,8 +26,18 @@ bool GLContextOSMesa::Initialize(GLSurface* compatible_surface, OSMesaContext share_handle = static_cast<OSMesaContext>( share_group() ? share_group()->GetHandle() : nullptr); - GLuint format = compatible_surface->GetFormat(); - DCHECK_NE(format, (unsigned)0); + GLuint format = 0; + switch (compatible_surface->GetFormat()) { + case GLSurface::SURFACE_OSMESA_BGRA: + format = OSMESA_BGRA; + break; + case GLSurface::SURFACE_OSMESA_RGBA: + format = OSMESA_RGBA; + break; + default: + NOTREACHED(); + return false; + } context_ = OSMesaCreateContextExt(format, 0, // depth bits 0, // stencil bits diff --git a/chromium/ui/gl/gl_fence.cc b/chromium/ui/gl/gl_fence.cc index 92aba194c21..62f098c2a83 100644 --- a/chromium/ui/gl/gl_fence.cc +++ b/chromium/ui/gl/gl_fence.cc @@ -64,4 +64,13 @@ GLFence* GLFence::Create() { return fence.release(); } +bool GLFence::ResetSupported() { + // Resetting a fence to its original state isn't supported by default. + return false; +} + +void GLFence::ResetState() { + NOTIMPLEMENTED(); +} + } // namespace gfx diff --git a/chromium/ui/gl/gl_fence.h b/chromium/ui/gl/gl_fence.h index cc25a1d735e..2f7a8772f9d 100644 --- a/chromium/ui/gl/gl_fence.h +++ b/chromium/ui/gl/gl_fence.h @@ -25,6 +25,12 @@ class GL_EXPORT GLFence { // client. virtual void ServerWait() = 0; + // Returns true if re-setting state is supported. + virtual bool ResetSupported(); + + // Resets the fence to the original state. + virtual void ResetState(); + private: DISALLOW_COPY_AND_ASSIGN(GLFence); }; diff --git a/chromium/ui/gl/gl_fence_nv.cc b/chromium/ui/gl/gl_fence_nv.cc index 0e23b283458..df972bd4b5e 100644 --- a/chromium/ui/gl/gl_fence_nv.cc +++ b/chromium/ui/gl/gl_fence_nv.cc @@ -20,6 +20,14 @@ GLFenceNV::GLFenceNV() { // they are bound, in that they acquire their state upon binding. // We will arbitrarily return TRUE for consistency. glGenFencesNV(1, &fence_); + ResetState(); +} + +bool GLFenceNV::ResetSupported() { + return true; +} + +void GLFenceNV::ResetState() { glSetFenceNV(fence_, GL_ALL_COMPLETED_NV); DCHECK(glIsFenceNV(fence_)); glFlush(); diff --git a/chromium/ui/gl/gl_fence_nv.h b/chromium/ui/gl/gl_fence_nv.h index b1dc88fb967..488251b777a 100644 --- a/chromium/ui/gl/gl_fence_nv.h +++ b/chromium/ui/gl/gl_fence_nv.h @@ -17,6 +17,8 @@ class GL_EXPORT GLFenceNV : public GLFence { ~GLFenceNV() override; // GLFence implementation: + bool ResetSupported() override; + void ResetState() override; bool HasCompleted() override; void ClientWait() override; void ServerWait() override; diff --git a/chromium/ui/gl/gl_image_io_surface.h b/chromium/ui/gl/gl_image_io_surface.h index d8b731828f2..7b68a96b6f5 100644 --- a/chromium/ui/gl/gl_image_io_surface.h +++ b/chromium/ui/gl/gl_image_io_surface.h @@ -5,6 +5,7 @@ #ifndef UI_GL_GL_IMAGE_IO_SURFACE_H_ #define UI_GL_GL_IMAGE_IO_SURFACE_H_ +#include <CoreVideo/CVPixelBuffer.h> #include <IOSurface/IOSurface.h> #include <stdint.h> @@ -31,6 +32,14 @@ class GL_EXPORT GLImageIOSurface : public GLImage { gfx::GenericSharedMemoryId io_surface_id, gfx::BufferFormat format); + // IOSurfaces coming from video decode are wrapped in a CVPixelBuffer + // and may be discarded if the owning CVPixelBuffer is destroyed. This + // initialization will ensure that the CVPixelBuffer be retained for the + // lifetime of the GLImage. + bool InitializeWithCVPixelBuffer(CVPixelBufferRef cv_pixel_buffer, + gfx::GenericSharedMemoryId io_surface_id, + gfx::BufferFormat format); + // Overridden from GLImage: void Destroy(bool have_context) override; gfx::Size GetSize() override; @@ -52,8 +61,7 @@ class GL_EXPORT GLImageIOSurface : public GLImage { gfx::GenericSharedMemoryId io_surface_id() const { return io_surface_id_; } base::ScopedCFTypeRef<IOSurfaceRef> io_surface(); - - static void SetLayerForWidget(gfx::AcceleratedWidget widget, CALayer* layer); + base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer(); static unsigned GetInternalFormatForTesting(gfx::BufferFormat format); @@ -61,21 +69,19 @@ class GL_EXPORT GLImageIOSurface : public GLImage { ~GLImageIOSurface() override; private: + class RGBConverter; + const gfx::Size size_; const unsigned internalformat_; gfx::BufferFormat format_; base::ScopedCFTypeRef<IOSurfaceRef> io_surface_; + base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer_; gfx::GenericSharedMemoryId io_surface_id_; base::ThreadChecker thread_checker_; - // GL state to support 420v IOSurface conversion to RGB. - unsigned framebuffer_ = 0; - unsigned vertex_shader_ = 0; - unsigned fragment_shader_ = 0; - unsigned program_ = 0; - int size_location_ = -1; - unsigned vertex_buffer_ = 0; - unsigned yuv_textures_[2] = {}; + // GL state to support 420v IOSurface conversion to RGB. This is retained + // to avoid re-creating the necessary GL programs every frame. + scoped_refptr<RGBConverter> rgb_converter_; 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 d0ca06b88e9..5d78bc67af7 100644 --- a/chromium/ui/gl/gl_image_io_surface.mm +++ b/chromium/ui/gl/gl_image_io_surface.mm @@ -6,16 +6,21 @@ #include <map> +#include "base/callback_helpers.h" #include "base/lazy_instance.h" +#include "base/mac/bind_objc_block.h" #include "base/mac/foundation_util.h" #include "base/strings/stringize_macros.h" +#include "base/strings/stringprintf.h" #include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/process_memory_dump.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_helper.h" +#include "ui/gl/scoped_api.h" #include "ui/gl/scoped_binders.h" +#include "ui/gl/scoped_cgl.h" // Note that this must be included after gl_bindings.h to avoid conflicts. #include <OpenGL/CGLIOSurface.h> @@ -27,8 +32,10 @@ using gfx::BufferFormat; namespace gl { namespace { -using WidgetToLayerMap = std::map<gfx::AcceleratedWidget, CALayer*>; -base::LazyInstance<WidgetToLayerMap> g_widget_to_layer_map; +const char kGLSLVersion[] = "#version 110"; + +const char kTextureRectangleRequired[] = + "#extension GL_ARB_texture_rectangle : require"; // clang-format off const char kVertexShader[] = @@ -183,6 +190,170 @@ GLenum DataType(BufferFormat format) { } // namespace +class GLImageIOSurface::RGBConverter + : public base::RefCounted<GLImageIOSurface::RGBConverter> { + public: + static scoped_refptr<RGBConverter> GetForCurrentContext(); + bool CopyTexImage(IOSurfaceRef io_surface, const gfx::Size& size); + + private: + friend class base::RefCounted<RGBConverter>; + RGBConverter(CGLContextObj cgl_context); + ~RGBConverter(); + + unsigned framebuffer_ = 0; + unsigned vertex_shader_ = 0; + unsigned fragment_shader_ = 0; + unsigned program_ = 0; + int size_location_ = -1; + unsigned vertex_buffer_ = 0; + base::ScopedTypeRef<CGLContextObj> cgl_context_; + + static base::LazyInstance< + std::map<CGLContextObj, GLImageIOSurface::RGBConverter*>> + g_rgb_converters; + static base::LazyInstance<base::ThreadChecker> + g_rgb_converters_thread_checker; +}; + +base::LazyInstance<std::map<CGLContextObj, GLImageIOSurface::RGBConverter*>> + GLImageIOSurface::RGBConverter::g_rgb_converters; + +base::LazyInstance<base::ThreadChecker> + GLImageIOSurface::RGBConverter::g_rgb_converters_thread_checker; + +scoped_refptr<GLImageIOSurface::RGBConverter> +GLImageIOSurface::RGBConverter::GetForCurrentContext() { + CGLContextObj current_context = CGLGetCurrentContext(); + DCHECK(current_context); + DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); + auto found = g_rgb_converters.Get().find(current_context); + if (found != g_rgb_converters.Get().end()) + return make_scoped_refptr(found->second); + return make_scoped_refptr(new RGBConverter(current_context)); +} + +GLImageIOSurface::RGBConverter::RGBConverter(CGLContextObj cgl_context) + : cgl_context_(cgl_context, base::scoped_policy::RETAIN) { + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; + glGenFramebuffersEXT(1, &framebuffer_); + vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer(); + vertex_shader_ = gfx::GLHelper::LoadShader( + GL_VERTEX_SHADER, + base::StringPrintf("%s\n%s", kGLSLVersion, kVertexShader).c_str()); + fragment_shader_ = gfx::GLHelper::LoadShader( + GL_FRAGMENT_SHADER, + base::StringPrintf("%s\n%s\n%s", kGLSLVersion, kTextureRectangleRequired, + kFragmentShader) + .c_str()); + program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_); + + gfx::ScopedUseProgram use_program(program_); + size_location_ = glGetUniformLocation(program_, "a_texScale"); + DCHECK_NE(-1, size_location_); + int y_sampler_location = glGetUniformLocation(program_, "a_y_texture"); + DCHECK_NE(-1, y_sampler_location); + int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture"); + DCHECK_NE(-1, uv_sampler_location); + + glUniform1i(y_sampler_location, 0); + glUniform1i(uv_sampler_location, 1); + + DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); + DCHECK(g_rgb_converters.Get().find(cgl_context) == + g_rgb_converters.Get().end()); + g_rgb_converters.Get()[cgl_context] = this; +} + +GLImageIOSurface::RGBConverter::~RGBConverter() { + DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); + DCHECK(g_rgb_converters.Get()[cgl_context_] == this); + g_rgb_converters.Get().erase(cgl_context_.get()); + { + gfx::ScopedCGLSetCurrentContext(cgl_context_.get()); + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; + glDeleteProgram(program_); + glDeleteShader(vertex_shader_); + glDeleteShader(fragment_shader_); + glDeleteBuffersARB(1, &vertex_buffer_); + glDeleteFramebuffersEXT(1, &framebuffer_); + } + cgl_context_.reset(); +} + +bool GLImageIOSurface::RGBConverter::CopyTexImage(IOSurfaceRef io_surface, + const gfx::Size& size) { + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; + DCHECK_EQ(CGLGetCurrentContext(), cgl_context_.get()); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, size.width(), size.height(), + 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + GLint target_texture = 0; + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &target_texture); + DCHECK(target_texture); + + // Note that state restoration is done explicitly in the ScopedClosureRunner + // instead of scoped binders to avoid https://crbug.com/601729. + GLint old_active_texture = -1; + glGetIntegerv(GL_ACTIVE_TEXTURE, &old_active_texture); + GLint old_texture0_binding = -1; + glActiveTexture(GL_TEXTURE0); + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture0_binding); + GLint old_texture1_binding = -1; + glActiveTexture(GL_TEXTURE1); + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture1_binding); + + unsigned y_texture = 0; + glGenTextures(1, &y_texture); + unsigned uv_texture = 0; + glGenTextures(1, &uv_texture); + + base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture0_binding); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture1_binding); + glActiveTexture(old_active_texture); + + glDeleteTextures(1, &y_texture); + glDeleteTextures(1, &uv_texture); + })); + + CGLError cgl_error = kCGLNoError; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, y_texture); + cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, + GL_RED, size.width(), size.height(), + GL_RED, GL_UNSIGNED_BYTE, io_surface, 0); + if (cgl_error != kCGLNoError) { + LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " + << cgl_error; + return false; + } + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, uv_texture); + cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, + GL_RG, size.width() / 2, size.height() / 2, + GL_RG, GL_UNSIGNED_BYTE, io_surface, 1); + if (cgl_error != kCGLNoError) { + LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " + << cgl_error; + return false; + } + + gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_); + gfx::ScopedViewport viewport(0, 0, size.width(), size.height()); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_RECTANGLE_ARB, target_texture, 0); + DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)); + gfx::ScopedUseProgram use_program(program_); + glUniform2f(size_location_, size.width(), size.height()); + gfx::GLHelper::DrawQuad(vertex_buffer_); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_RECTANGLE_ARB, 0, 0); + return true; +} + GLImageIOSurface::GLImageIOSurface(const gfx::Size& size, unsigned internalformat) : size_(size), @@ -216,17 +387,27 @@ bool GLImageIOSurface::Initialize(IOSurfaceRef io_surface, return true; } +bool GLImageIOSurface::InitializeWithCVPixelBuffer( + CVPixelBufferRef cv_pixel_buffer, + gfx::GenericSharedMemoryId io_surface_id, + BufferFormat format) { + IOSurfaceRef io_surface = CVPixelBufferGetIOSurface(cv_pixel_buffer); + if (!io_surface) { + LOG(ERROR) << "Can't init GLImage from CVPixelBuffer with no IOSurface"; + return false; + } + + if (!Initialize(io_surface, io_surface_id, format)) + return false; + + cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN); + return true; +} + void GLImageIOSurface::Destroy(bool have_context) { DCHECK(thread_checker_.CalledOnValidThread()); - if (have_context && framebuffer_) { - glDeleteProgram(program_); - glDeleteShader(vertex_shader_); - glDeleteShader(fragment_shader_); - glDeleteBuffersARB(1, &vertex_buffer_); - glDeleteFramebuffersEXT(1, &framebuffer_); - glDeleteTextures(2, yuv_textures_); - } io_surface_.reset(); + cv_pixel_buffer_.reset(); } gfx::Size GLImageIOSurface::GetSize() { @@ -274,92 +455,13 @@ bool GLImageIOSurface::CopyTexImage(unsigned target) { if (format_ != BufferFormat::YUV_420_BIPLANAR) return false; - - if (target != GL_TEXTURE_2D) { - LOG(ERROR) << "YUV_420_BIPLANAR requires TEXTURE_2D target"; + if (target != GL_TEXTURE_RECTANGLE_ARB) { + LOG(ERROR) << "YUV_420_BIPLANAR requires GL_TEXTURE_RECTANGLE_ARB target"; return false; } - if (!framebuffer_) { - glGenFramebuffersEXT(1, &framebuffer_); - vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer(); - vertex_shader_ = gfx::GLHelper::LoadShader(GL_VERTEX_SHADER, kVertexShader); - fragment_shader_ = - gfx::GLHelper::LoadShader(GL_FRAGMENT_SHADER, kFragmentShader); - program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_); - gfx::ScopedUseProgram use_program(program_); - - size_location_ = glGetUniformLocation(program_, "a_texScale"); - DCHECK_NE(-1, size_location_); - int y_sampler_location = glGetUniformLocation(program_, "a_y_texture"); - DCHECK_NE(-1, y_sampler_location); - int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture"); - DCHECK_NE(-1, uv_sampler_location); - - glUniform1i(y_sampler_location, 0); - glUniform1i(uv_sampler_location, 1); - - glGenTextures(2, yuv_textures_); - DCHECK(yuv_textures_[0]); - DCHECK(yuv_textures_[1]); - } - - CGLContextObj cgl_context = - static_cast<CGLContextObj>(gfx::GLContext::GetCurrent()->GetHandle()); - - GLint target_texture = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &target_texture); - DCHECK(target_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size_.width(), size_.height(), 0, - GL_RGB, GL_UNSIGNED_BYTE, nullptr); - - CGLError cgl_error = kCGLNoError; - { - DCHECK(io_surface_); - - gfx::ScopedActiveTexture active_texture0(GL_TEXTURE0); - gfx::ScopedTextureBinder texture_y_binder(GL_TEXTURE_RECTANGLE_ARB, - yuv_textures_[0]); - cgl_error = CGLTexImageIOSurface2D( - cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RED, size_.width(), - size_.height(), GL_RED, GL_UNSIGNED_BYTE, io_surface_.get(), 0); - if (cgl_error != kCGLNoError) { - LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " - << cgl_error; - return false; - } - { - gfx::ScopedActiveTexture active_texture1(GL_TEXTURE1); - gfx::ScopedTextureBinder texture_uv_binder(GL_TEXTURE_RECTANGLE_ARB, - yuv_textures_[1]); - cgl_error = CGLTexImageIOSurface2D( - cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RG, size_.width() / 2, - size_.height() / 2, GL_RG, GL_UNSIGNED_BYTE, io_surface_.get(), 1); - if (cgl_error != kCGLNoError) { - LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " - << cgl_error; - return false; - } - - gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_); - gfx::ScopedViewport viewport(0, 0, size_.width(), size_.height()); - glViewport(0, 0, size_.width(), size_.height()); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, target_texture, 0); - DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), - glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)); - - gfx::ScopedUseProgram use_program(program_); - glUniform2f(size_location_, size_.width(), size_.height()); - - gfx::GLHelper::DrawQuad(vertex_buffer_); - - // Detach the output texture from the fbo. - glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, 0, 0); - } - } - return true; + rgb_converter_ = RGBConverter::GetForCurrentContext(); + return rgb_converter_->CopyTexImage(io_surface_.get(), size_); } bool GLImageIOSurface::CopyTexSubImage(unsigned target, @@ -400,13 +502,8 @@ base::ScopedCFTypeRef<IOSurfaceRef> GLImageIOSurface::io_surface() { return io_surface_; } -// static -void GLImageIOSurface::SetLayerForWidget(gfx::AcceleratedWidget widget, - CALayer* layer) { - if (layer) - g_widget_to_layer_map.Pointer()->insert(std::make_pair(widget, layer)); - else - g_widget_to_layer_map.Pointer()->erase(widget); +base::ScopedCFTypeRef<CVPixelBufferRef> GLImageIOSurface::cv_pixel_buffer() { + return cv_pixel_buffer_; } // static diff --git a/chromium/ui/gl/gl_image_io_surface_unittest.cc b/chromium/ui/gl/gl_image_io_surface_unittest.cc index 487366fd8a1..6028e3feb88 100644 --- a/chromium/ui/gl/gl_image_io_surface_unittest.cc +++ b/chromium/ui/gl/gl_image_io_surface_unittest.cc @@ -17,6 +17,16 @@ namespace { template <gfx::BufferFormat format> class GLImageIOSurfaceTestDelegate { public: + scoped_refptr<GLImage> CreateImage(const gfx::Size& size) const { + scoped_refptr<GLImageIOSurface> image(new GLImageIOSurface( + size, GLImageIOSurface::GetInternalFormatForTesting(format))); + IOSurfaceRef surface_ref = gfx::CreateIOSurface(size, format); + bool rv = + image->Initialize(surface_ref, gfx::GenericSharedMemoryId(1), format); + EXPECT_TRUE(rv); + return image; + } + scoped_refptr<GLImage> CreateSolidColorImage(const gfx::Size& size, const uint8_t color[4]) const { scoped_refptr<GLImageIOSurface> image(new GLImageIOSurface( @@ -40,6 +50,8 @@ class GLImageIOSurfaceTestDelegate { return image; } + + unsigned GetTextureTarget() const { return GL_TEXTURE_RECTANGLE_ARB; } }; using GLImageTestTypes = testing::Types< @@ -49,6 +61,14 @@ using GLImageTestTypes = testing::Types< INSTANTIATE_TYPED_TEST_CASE_P(GLImageIOSurface, GLImageTest, GLImageTestTypes); +using GLImageIOSurfaceTestTypes = + testing::Types<GLImageIOSurfaceTestDelegate<gfx::BufferFormat::RGBA_8888>, + GLImageIOSurfaceTestDelegate<gfx::BufferFormat::BGRA_8888>>; + +INSTANTIATE_TYPED_TEST_CASE_P(GLImageIOSurface, + GLImageZeroInitializeTest, + GLImageIOSurfaceTestTypes); + INSTANTIATE_TYPED_TEST_CASE_P( GLImageIOSurface, GLImageCopyTest, diff --git a/chromium/ui/gl/gl_image_memory.cc b/chromium/ui/gl/gl_image_memory.cc index 7cf74c82834..a29a388585f 100644 --- a/chromium/ui/gl/gl_image_memory.cc +++ b/chromium/ui/gl/gl_image_memory.cc @@ -261,7 +261,8 @@ scoped_ptr<uint8_t[]> GLES2Data(const gfx::Size& size, case BufferFormat::R_8: { size_t gles2_data_stride = RowSizeForBufferFormat(size.width(), format, 0); - if (stride == gles2_data_stride) + if (stride == gles2_data_stride || + gfx::g_driver_gl.ext.b_GL_EXT_unpack_subimage) return nullptr; // No data conversion needed scoped_ptr<uint8_t[]> gles2_data( diff --git a/chromium/ui/gl/gl_image_ozone_native_pixmap.cc b/chromium/ui/gl/gl_image_ozone_native_pixmap.cc index 42e96a6e1ca..aeeca9f3850 100644 --- a/chromium/ui/gl/gl_image_ozone_native_pixmap.cc +++ b/chromium/ui/gl/gl_image_ozone_native_pixmap.cc @@ -142,6 +142,19 @@ void GLImageOzoneNativePixmap::Destroy(bool have_context) { gl::GLImageEGL::Destroy(have_context); } +bool GLImageOzoneNativePixmap::CopyTexImage(unsigned target) { + if (egl_image_ == EGL_NO_IMAGE_KHR) { + // Pass-through image type fails to bind and copy; make sure we + // don't draw with uninitialized texture. + std::vector<unsigned char> data(size_.width() * size_.height() * 4); + glTexImage2D(target, 0, GL_RGBA, size_.width(), + size_.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, + data.data()); + return true; + } + return GLImageEGL::CopyTexImage(target); +} + bool GLImageOzoneNativePixmap::ScheduleOverlayPlane(AcceleratedWidget widget, int z_order, OverlayTransform transform, diff --git a/chromium/ui/gl/gl_image_ozone_native_pixmap.h b/chromium/ui/gl/gl_image_ozone_native_pixmap.h index 318c8ef3df7..469c04415af 100644 --- a/chromium/ui/gl/gl_image_ozone_native_pixmap.h +++ b/chromium/ui/gl/gl_image_ozone_native_pixmap.h @@ -22,6 +22,7 @@ class GL_EXPORT GLImageOzoneNativePixmap : public gl::GLImageEGL { // Overridden from GLImage: unsigned GetInternalFormat() override; void Destroy(bool have_context) override; + bool CopyTexImage(unsigned target) override; bool ScheduleOverlayPlane(AcceleratedWidget widget, int z_order, OverlayTransform transform, diff --git a/chromium/ui/gl/gl_image_ozone_native_pixmap_unittest.cc b/chromium/ui/gl/gl_image_ozone_native_pixmap_unittest.cc index a6784152145..223ba698026 100644 --- a/chromium/ui/gl/gl_image_ozone_native_pixmap_unittest.cc +++ b/chromium/ui/gl/gl_image_ozone_native_pixmap_unittest.cc @@ -31,6 +31,8 @@ class GLImageOzoneNativePixmapTestDelegate { EXPECT_TRUE(image->Initialize(pixmap.get(), pixmap->GetBufferFormat())); return image; } + + unsigned GetTextureTarget() const { return GL_TEXTURE_2D; } }; INSTANTIATE_TYPED_TEST_CASE_P(GLImageOzoneNativePixmap, diff --git a/chromium/ui/gl/gl_image_ref_counted_memory_unittest.cc b/chromium/ui/gl/gl_image_ref_counted_memory_unittest.cc index 5d5bdd3adb4..f24b1ecfa37 100644 --- a/chromium/ui/gl/gl_image_ref_counted_memory_unittest.cc +++ b/chromium/ui/gl/gl_image_ref_counted_memory_unittest.cc @@ -33,6 +33,8 @@ class GLImageRefCountedMemoryTestDelegate { EXPECT_TRUE(rv); return image; } + + unsigned GetTextureTarget() const { return GL_TEXTURE_2D; } }; using GLImageTestTypes = testing::Types< diff --git a/chromium/ui/gl/gl_image_shared_memory_unittest.cc b/chromium/ui/gl/gl_image_shared_memory_unittest.cc index 57371ad289c..ae33e3845b9 100644 --- a/chromium/ui/gl/gl_image_shared_memory_unittest.cc +++ b/chromium/ui/gl/gl_image_shared_memory_unittest.cc @@ -38,6 +38,8 @@ class GLImageSharedMemoryTestDelegate { EXPECT_TRUE(rv); return image; } + + unsigned GetTextureTarget() const { return GL_TEXTURE_2D; } }; using GLImageTestTypes = testing::Types< @@ -86,6 +88,8 @@ class GLImageSharedMemoryPoolTestDelegate { EXPECT_TRUE(rv); return image; } + + unsigned GetTextureTarget() const { return GL_TEXTURE_2D; } }; INSTANTIATE_TYPED_TEST_CASE_P(GLImageSharedMemoryPool, diff --git a/chromium/ui/gl/gl_implementation.cc b/chromium/ui/gl/gl_implementation.cc index 40aedde7b6e..af06cbee981 100644 --- a/chromium/ui/gl/gl_implementation.cc +++ b/chromium/ui/gl/gl_implementation.cc @@ -53,7 +53,7 @@ void CleanupNativeLibraries(void* unused) { } } -} +} // namespace base::ThreadLocalPointer<GLApi>* g_current_gl_context_tls = NULL; OSMESAApi* g_current_osmesa_context; diff --git a/chromium/ui/gl/gl_implementation_x11.cc b/chromium/ui/gl/gl_implementation_x11.cc index 2371c083d76..682f9f9b6d5 100644 --- a/chromium/ui/gl/gl_implementation_x11.cc +++ b/chromium/ui/gl/gl_implementation_x11.cc @@ -42,8 +42,8 @@ const char kGLLibraryName[] = "libGL.so.1"; const char kGLESv2LibraryName[] = "libGLESv2.so.2"; const char kEGLLibraryName[] = "libEGL.so.1"; -const char kGLESv2ANGLELibraryName[] = "libGLESv2_ANGLE.so"; -const char kEGLANGLELibraryName[] = "libEGL_ANGLE.so"; +const char kGLESv2ANGLELibraryName[] = "libGLESv2.so"; +const char kEGLANGLELibraryName[] = "libEGL.so"; } // namespace diff --git a/chromium/ui/gl/gl_mock.cc b/chromium/ui/gl/gl_mock.cc index 3b835792202..785376fd4f7 100644 --- a/chromium/ui/gl/gl_mock.cc +++ b/chromium/ui/gl/gl_mock.cc @@ -6,6 +6,17 @@ namespace gfx { +namespace { + +// This is called mainly to prevent the compiler combining the code of mock +// functions with identical contents, so that their function pointers will be +// different. +void MakeFunctionUnique(const char* func_name) { + VLOG(2) << "Calling mock " << func_name; +} + +} // namespace anonymous + MockGLInterface::MockGLInterface() { } @@ -18,4 +29,13 @@ void MockGLInterface::SetGLInterface(MockGLInterface* gl_interface) { interface_ = gl_interface; } +void GL_BINDING_CALL MockGLInterface::Mock_glTexSubImage3DNoData( + GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type) { + MakeFunctionUnique("glTexSubImage3DNoData"); + interface_->TexSubImage3DNoData( + target, level, xoffset, yoffset, zoffset, width, height, depth, + format, type); +} + } // namespace gfx diff --git a/chromium/ui/gl/gl_mock.h b/chromium/ui/gl/gl_mock.h index 2a375e6768f..1ef09fabfcd 100644 --- a/chromium/ui/gl/gl_mock.h +++ b/chromium/ui/gl/gl_mock.h @@ -42,19 +42,40 @@ class MockGLInterface { const void* /*data*/) { NOTREACHED(); } + void TexSubImage3D( - GLenum /*target*/, GLint /*level*/, GLint /*xoffset*/, GLint /*yoffset*/, - GLint /*zoffset*/, GLsizei /*width*/, GLsizei /*height*/, - GLsizei /*depth*/, GLenum /*format*/, GLenum /*type*/, - const void* /*pixels*/) { - NOTREACHED(); + GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, + const void* pixels) { + if (pixels == nullptr) { + TexSubImage3DNoData(target, level, xoffset, yoffset, zoffset, + width, height, depth, format, type); + } else { + NOTREACHED(); + } } + MOCK_METHOD10(TexSubImage3DNoData, + void(GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLenum type)); + private: static MockGLInterface* interface_; // Static mock functions that invoke the member functions of interface_. #include "gl_bindings_autogen_mock.h" + + static void GL_BINDING_CALL Mock_glTexSubImage3DNoData( + GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type); }; } // namespace gfx diff --git a/chromium/ui/gl/gl_mock_autogen_gl.h b/chromium/ui/gl/gl_mock_autogen_gl.h index 830425c360e..2a5a4457433 100644 --- a/chromium/ui/gl/gl_mock_autogen_gl.h +++ b/chromium/ui/gl/gl_mock_autogen_gl.h @@ -522,6 +522,7 @@ MOCK_METHOD2(PixelStorei, void(GLenum pname, GLint param)); MOCK_METHOD2(PointParameteri, void(GLenum pname, GLint param)); MOCK_METHOD2(PolygonOffset, void(GLfloat factor, GLfloat units)); MOCK_METHOD0(PopGroupMarkerEXT, void()); +MOCK_METHOD1(PrimitiveRestartIndex, void(GLuint index)); MOCK_METHOD4(ProgramBinary, void(GLuint program, GLenum binaryFormat, diff --git a/chromium/ui/gl/gl_surface.cc b/chromium/ui/gl/gl_surface.cc index a20356ec1bf..da0831528b7 100644 --- a/chromium/ui/gl/gl_surface.cc +++ b/chromium/ui/gl/gl_surface.cc @@ -96,6 +96,10 @@ bool GLSurface::InitializeOneOffImplementation(GLImplementation impl, GLSurface::GLSurface() {} bool GLSurface::Initialize() { + return Initialize(SURFACE_DEFAULT); +} + +bool GLSurface::Initialize(GLSurface::Format format) { return true; } @@ -186,9 +190,9 @@ void* GLSurface::GetConfig() { return NULL; } -unsigned GLSurface::GetFormat() { +GLSurface::Format GLSurface::GetFormat() { NOTIMPLEMENTED(); - return 0; + return SURFACE_DEFAULT; } VSyncProvider* GLSurface::GetVSyncProvider() { @@ -226,6 +230,10 @@ bool GLSurface::FlipsVertically() const { return false; } +bool GLSurface::BuffersFlipped() const { + return false; +} + GLSurface* GLSurface::GetCurrent() { return current_surface_.Pointer()->Get(); } @@ -257,8 +265,8 @@ void GLSurface::OnSetSwapInterval(int interval) { GLSurfaceAdapter::GLSurfaceAdapter(GLSurface* surface) : surface_(surface) {} -bool GLSurfaceAdapter::Initialize() { - return surface_->Initialize(); +bool GLSurfaceAdapter::Initialize(GLSurface::Format format) { + return surface_->Initialize(format); } void GLSurfaceAdapter::Destroy() { @@ -365,7 +373,7 @@ void* GLSurfaceAdapter::GetConfig() { return surface_->GetConfig(); } -unsigned GLSurfaceAdapter::GetFormat() { +GLSurface::Format GLSurfaceAdapter::GetFormat() { return surface_->GetFormat(); } @@ -390,6 +398,10 @@ bool GLSurfaceAdapter::FlipsVertically() const { return surface_->FlipsVertically(); } +bool GLSurfaceAdapter::BuffersFlipped() const { + return surface_->BuffersFlipped(); +} + GLSurfaceAdapter::~GLSurfaceAdapter() {} } // namespace gfx diff --git a/chromium/ui/gl/gl_surface.h b/chromium/ui/gl/gl_surface.h index d84f391e1ee..6fa69478e18 100644 --- a/chromium/ui/gl/gl_surface.h +++ b/chromium/ui/gl/gl_surface.h @@ -36,11 +36,22 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { public: GLSurface(); + // Minimum bit depth of surface. + enum Format { + SURFACE_ARGB8888, + SURFACE_RGB565, + SURFACE_OSMESA_BGRA, + SURFACE_OSMESA_RGBA, + SURFACE_SURFACELESS, + SURFACE_DEFAULT = SURFACE_ARGB8888 + }; + // (Re)create the surface. TODO(apatrick): This is an ugly hack to allow the // EGL surface associated to be recreated without destroying the associated // context. The implementation of this function for other GLSurface derived // classes is in a pending changelist. virtual bool Initialize(); + virtual bool Initialize(GLSurface::Format format); // Destroys the surface. virtual void Destroy() = 0; @@ -52,8 +63,8 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { // Recreate the surface without changing the size. virtual bool Recreate(); - // Unschedule the GpuScheduler and return true to abort the processing of - // a GL draw call to this surface and defer it until the GpuScheduler is + // Unschedule the CommandExecutor and return true to abort the processing of + // a GL draw call to this surface and defer it until the CommandExecutor is // rescheduled. virtual bool DeferDraws(); @@ -144,7 +155,7 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { virtual void* GetConfig(); // Get the GL pixel format of the surface, if available. - virtual unsigned GetFormat(); + virtual GLSurface::Format GetFormat(); // Get access to a helper providing time of recent refresh and period // of screen refresh. If unavailable, returns NULL. @@ -184,6 +195,10 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { virtual bool FlipsVertically() const; + // Returns true if SwapBuffers or PostSubBuffers causes a flip, such that + // the next buffer may be 2 frames old. + virtual bool BuffersFlipped() const; + // Create a GL surface that renders directly to a view. static scoped_refptr<GLSurface> CreateViewGLSurface( gfx::AcceleratedWidget window); @@ -231,7 +246,7 @@ class GL_EXPORT GLSurfaceAdapter : public GLSurface { public: explicit GLSurfaceAdapter(GLSurface* surface); - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, @@ -262,7 +277,7 @@ class GL_EXPORT GLSurfaceAdapter : public GLSurface { void* GetShareHandle() override; void* GetDisplay() override; void* GetConfig() override; - unsigned GetFormat() override; + GLSurface::Format GetFormat() override; VSyncProvider* GetVSyncProvider() override; bool ScheduleOverlayPlane(int z_order, OverlayTransform transform, @@ -271,6 +286,7 @@ class GL_EXPORT GLSurfaceAdapter : public GLSurface { const RectF& crop_rect) override; bool IsSurfaceless() const override; bool FlipsVertically() const override; + bool BuffersFlipped() const override; GLSurface* surface() const { return surface_.get(); } diff --git a/chromium/ui/gl/gl_surface_android.cc b/chromium/ui/gl/gl_surface_android.cc index 4bd113b2b32..1486ec4583d 100644 --- a/chromium/ui/gl/gl_surface_android.cc +++ b/chromium/ui/gl/gl_surface_android.cc @@ -59,7 +59,7 @@ scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( switch (GetGLImplementation()) { case kGLImplementationOSMesaGL: { scoped_refptr<GLSurface> surface( - new GLSurfaceOSMesa(OSMesaSurfaceFormatBGRA, size)); + new GLSurfaceOSMesa(SURFACE_OSMESA_BGRA, size)); if (!surface->Initialize()) return NULL; diff --git a/chromium/ui/gl/gl_surface_egl.cc b/chromium/ui/gl/gl_surface_egl.cc index 20f62986b46..0db0467e555 100644 --- a/chromium/ui/gl/gl_surface_egl.cc +++ b/chromium/ui/gl/gl_surface_egl.cc @@ -7,6 +7,9 @@ #include <stddef.h> #include <stdint.h> +#include <map> +#include <vector> + #include "base/command_line.h" #include "base/logging.h" #include "base/macros.h" @@ -14,6 +17,7 @@ #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" +#include "base/sys_info.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "ui/gfx/geometry/rect.h" @@ -30,18 +34,13 @@ #include <android/native_window_jni.h> #endif -#if defined (USE_OZONE) -#include "ui/ozone/public/ozone_platform.h" -#include "ui/ozone/public/surface_factory_ozone.h" -#endif - #if defined(USE_X11) && !defined(OS_CHROMEOS) extern "C" { #include <X11/Xlib.h> #define Status int } #include "ui/base/x/x11_util_internal.h" -#include "ui/gfx/x/x11_switches.h" +#include "ui/gfx/x/x11_switches.h" // nogncheck #endif #if !defined(EGL_FIXED_SIZE_ANGLE) @@ -116,7 +115,6 @@ const unsigned int MULTISWAP_FRAME_VSYNC_THRESHOLD = 60; namespace { -EGLConfig g_config; EGLDisplay g_display; EGLNativeDisplayType g_native_display; @@ -197,9 +195,6 @@ EGLDisplay GetDisplayFromType(DisplayType display_type, case DEFAULT: case SWIFT_SHADER: return eglGetDisplay(native_display); - case ANGLE_WARP: - return GetPlatformANGLEDisplay(native_display, - EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, true); case ANGLE_D3D9: return GetPlatformANGLEDisplay(native_display, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, false); @@ -224,8 +219,6 @@ const char* DisplayTypeString(DisplayType display_type) { return "Default"; case SWIFT_SHADER: return "SwiftShader"; - case ANGLE_WARP: - return "WARP"; case ANGLE_D3D9: return "D3D9"; case ANGLE_D3D11: @@ -240,6 +233,156 @@ const char* DisplayTypeString(DisplayType display_type) { } } +bool ValidateEglConfig(EGLDisplay display, + const EGLint* config_attribs, + EGLint* num_configs) { + if (!eglChooseConfig(display, + config_attribs, + NULL, + 0, + num_configs)) { + LOG(ERROR) << "eglChooseConfig failed with error " + << GetLastEGLErrorString(); + return false; + } + if (*num_configs == 0) { + LOG(ERROR) << "No suitable EGL configs found."; + return false; + } + return true; +} + +EGLConfig ChooseConfig(GLSurface::Format format) { + static std::map<GLSurface::Format, EGLConfig> config_map; + + if (config_map.find(format) != config_map.end()) { + return config_map[format]; + } + + // Choose an EGL configuration. + // On X this is only used for PBuffer surfaces. + EGLint renderable_type = EGL_OPENGL_ES2_BIT; + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableUnsafeES3APIs)) { + renderable_type = EGL_OPENGL_ES3_BIT; + } + + EGLint buffer_size = 32; + EGLint alpha_size = 8; + +#if defined(USE_X11) && !defined(OS_CHROMEOS) + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kWindowDepth)) { + std::string depth = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kWindowDepth); + + bool succeed = base::StringToInt(depth, &buffer_size); + DCHECK(succeed); + + alpha_size = buffer_size == 32 ? 8 : 0; + } +#endif + + EGLint surface_type = (format == GLSurface::SURFACE_SURFACELESS) + ? EGL_DONT_CARE + : EGL_WINDOW_BIT | EGL_PBUFFER_BIT; + + EGLint config_attribs_8888[] = { + EGL_BUFFER_SIZE, buffer_size, + EGL_ALPHA_SIZE, alpha_size, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_RENDERABLE_TYPE, renderable_type, + EGL_SURFACE_TYPE, surface_type, + EGL_NONE + }; + + EGLint* choose_attributes = config_attribs_8888; + EGLint config_attribs_565[] = { + EGL_BUFFER_SIZE, 16, + EGL_BLUE_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_RED_SIZE, 5, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, surface_type, + EGL_NONE + }; + if (format == GLSurface::SURFACE_RGB565) { + choose_attributes = config_attribs_565; + } + + EGLint num_configs; + EGLint config_size = 1; + EGLConfig config = nullptr; + EGLConfig* config_data = &config; + // Validate if there are any configs for given attribs. + if (!ValidateEglConfig(g_display, choose_attributes, &num_configs)) { + return config; + } + + scoped_ptr<EGLConfig[]> matching_configs(new EGLConfig[num_configs]); + if (format == GLSurface::SURFACE_RGB565) { + config_size = num_configs; + config_data = matching_configs.get(); + } + + if (!eglChooseConfig(g_display, choose_attributes, config_data, config_size, + &num_configs)) { + LOG(ERROR) << "eglChooseConfig failed with error " + << GetLastEGLErrorString(); + return config; + } + + if (format == GLSurface::SURFACE_RGB565) { + // Because of the EGL config sort order, we have to iterate + // through all of them (it'll put higher sum(R,G,B) bits + // first with the above attribs). + bool match_found = false; + for (int i = 0; i < num_configs; i++) { + EGLint red, green, blue, alpha; + // Read the relevant attributes of the EGLConfig. + if (eglGetConfigAttrib(g_display, matching_configs[i], + EGL_RED_SIZE, &red) && + eglGetConfigAttrib(g_display, matching_configs[i], + EGL_BLUE_SIZE, &blue) && + eglGetConfigAttrib(g_display, matching_configs[i], + EGL_GREEN_SIZE, &green) && + eglGetConfigAttrib(g_display, matching_configs[i], + EGL_ALPHA_SIZE, &alpha) && + alpha == 0 && + red == 5 && + green == 6 && + blue == 5) { + config = matching_configs[i]; + match_found = true; + break; + } + } + if (!match_found) { + // To fall back to default 32 bit format, choose with + // the right attributes again. + if (!ValidateEglConfig(g_display, + config_attribs_8888, + &num_configs)) { + return config; + } + if (!eglChooseConfig(g_display, + config_attribs_8888, + &config, + 1, + &num_configs)) { + LOG(ERROR) << "eglChooseConfig failed with error " + << GetLastEGLErrorString(); + return config; + } + } + } + config_map[format] = config; + return config; +} + } // namespace void GetEGLInitDisplays(bool supports_angle_d3d, @@ -274,9 +417,6 @@ void GetEGLInitDisplays(bool supports_angle_d3d, if (requested_renderer == kANGLEImplementationD3D9Name) { init_displays->push_back(ANGLE_D3D9); } - if (requested_renderer == kANGLEImplementationWARPName) { - init_displays->push_back(ANGLE_WARP); - } } } @@ -312,76 +452,6 @@ bool GLSurfaceEGL::InitializeOneOff() { if (g_display == EGL_NO_DISPLAY) return false; - // Choose an EGL configuration. - // On X this is only used for PBuffer surfaces. - EGLint renderable_type = EGL_OPENGL_ES2_BIT; - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableUnsafeES3APIs)) { - renderable_type = EGL_OPENGL_ES3_BIT; - } - - EGLint buffer_size = 32; - EGLint alpha_size = 8; - -#if defined(USE_X11) && !defined(OS_CHROMEOS) - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kWindowDepth)) { - std::string depth = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kWindowDepth); - - bool succeed = base::StringToInt(depth, &buffer_size); - DCHECK(succeed); - - alpha_size = buffer_size == 32 ? 8 : 0; - } -#endif - - const EGLint kConfigAttribs[] = { - EGL_BUFFER_SIZE, buffer_size, - EGL_ALPHA_SIZE, alpha_size, - EGL_BLUE_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_RED_SIZE, 8, - EGL_RENDERABLE_TYPE, renderable_type, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, - EGL_NONE - }; - -#if defined(USE_OZONE) - const EGLint* config_attribs = ui::OzonePlatform::GetInstance() - ->GetSurfaceFactoryOzone() - ->GetEGLSurfaceProperties(kConfigAttribs); -#else - const EGLint* config_attribs = kConfigAttribs; -#endif - - EGLint num_configs; - if (!eglChooseConfig(g_display, - config_attribs, - NULL, - 0, - &num_configs)) { - LOG(ERROR) << "eglChooseConfig failed with error " - << GetLastEGLErrorString(); - return false; - } - - if (num_configs == 0) { - LOG(ERROR) << "No suitable EGL configs found."; - return false; - } - - if (!eglChooseConfig(g_display, - config_attribs, - &g_config, - 1, - &num_configs)) { - LOG(ERROR) << "eglChooseConfig failed with error " - << GetLastEGLErrorString(); - return false; - } - g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS); g_egl_create_context_robustness_supported = HasEGLExtension("EGL_EXT_create_context_robustness"); @@ -397,8 +467,8 @@ bool GLSurfaceEGL::InitializeOneOff() { g_use_direct_composition = HasEGLExtension("EGL_ANGLE_direct_composition") && HasEGLExtension("EGL_ANGLE_flexible_surface_compatibility") && - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUseDirectComposition); + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableDirectComposition); // TODO(oetuaho@nvidia.com): Surfaceless is disabled on Android as a temporary // workaround, since code written for Android WebView takes different paths @@ -434,10 +504,21 @@ bool GLSurfaceEGL::InitializeOneOff() { return true; } +GLSurface::Format GLSurfaceEGL::GetFormat() { + return format_; +} + EGLDisplay GLSurfaceEGL::GetDisplay() { return g_display; } +EGLConfig GLSurfaceEGL::GetConfig() { + if (!config_) { + config_ = ChooseConfig(format_); + } + return config_; +} + EGLDisplay GLSurfaceEGL::GetHardwareDisplay() { return g_display; } @@ -526,9 +607,7 @@ EGLDisplay GLSurfaceEGL::InitializeDisplay() { NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window) : window_(window), - config_(NULL), size_(1, 1), - alpha_(true), enable_fixed_size_angle_(false), surface_(NULL), supports_post_sub_buffer_(false), @@ -548,7 +627,8 @@ NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window) #endif } -bool NativeViewGLSurfaceEGL::Initialize() { +bool NativeViewGLSurfaceEGL::Initialize(GLSurface::Format format) { + format_ = format; return Initialize(nullptr); } @@ -644,10 +724,6 @@ void NativeViewGLSurfaceEGL::Destroy() { } } -EGLConfig NativeViewGLSurfaceEGL::GetConfig() { - return g_config; -} - bool NativeViewGLSurfaceEGL::IsOffscreen() { return false; } @@ -722,10 +798,9 @@ gfx::Size NativeViewGLSurfaceEGL::GetSize() { bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size, float scale_factor, bool has_alpha) { - if (size == GetSize() && has_alpha == alpha_) + if (size == GetSize()) return true; - alpha_ = has_alpha; size_ = size; scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current; @@ -740,7 +815,7 @@ bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size, Destroy(); - if (!Initialize()) { + if (!Initialize(format_)) { LOG(ERROR) << "Failed to resize window."; return false; } @@ -750,7 +825,7 @@ bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size, bool NativeViewGLSurfaceEGL::Recreate() { Destroy(); - if (!Initialize()) { + if (!Initialize(format_)) { LOG(ERROR) << "Failed to create surface."; return false; } @@ -769,6 +844,10 @@ bool NativeViewGLSurfaceEGL::FlipsVertically() const { return flips_vertically_; } +bool NativeViewGLSurfaceEGL::BuffersFlipped() const { + return g_use_direct_composition; +} + gfx::SwapResult NativeViewGLSurfaceEGL::PostSubBuffer(int x, int y, int width, @@ -861,7 +940,21 @@ PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size& size) } bool PbufferGLSurfaceEGL::Initialize() { + GLSurface::Format format = SURFACE_DEFAULT; +#if defined(OS_ANDROID) + // This is to allow context virtualization which requires on- and offscreen + // to use a compatible config. We expect the client to request RGB565 + // onscreen surface also for this to work (with the exception of + // fullscreen video). + if (base::SysInfo::IsLowEndDevice()) + format = SURFACE_RGB565; +#endif + return Initialize(format); +} + +bool PbufferGLSurfaceEGL::Initialize(GLSurface::Format format) { EGLSurface old_surface = surface_; + format_ = format; EGLDisplay display = GetDisplay(); if (!display) { @@ -912,10 +1005,6 @@ void PbufferGLSurfaceEGL::Destroy() { } } -EGLConfig PbufferGLSurfaceEGL::GetConfig() { - return g_config; -} - bool PbufferGLSurfaceEGL::IsOffscreen() { return true; } @@ -946,7 +1035,7 @@ bool PbufferGLSurfaceEGL::Resize(const gfx::Size& size, size_ = size; - if (!Initialize()) { + if (!Initialize(format_)) { LOG(ERROR) << "Failed to resize pbuffer."; return false; } @@ -987,17 +1076,19 @@ PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() { SurfacelessEGL::SurfacelessEGL(const gfx::Size& size) : size_(size) { + format_ = GLSurface::SURFACE_SURFACELESS; } bool SurfacelessEGL::Initialize() { - return true; + return Initialize(SURFACE_SURFACELESS); } -void SurfacelessEGL::Destroy() { +bool SurfacelessEGL::Initialize(GLSurface::Format format) { + format_ = format; + return true; } -EGLConfig SurfacelessEGL::GetConfig() { - return g_config; +void SurfacelessEGL::Destroy() { } bool SurfacelessEGL::IsOffscreen() { diff --git a/chromium/ui/gl/gl_surface_egl.h b/chromium/ui/gl/gl_surface_egl.h index 45396d47229..97f2b15384e 100644 --- a/chromium/ui/gl/gl_surface_egl.h +++ b/chromium/ui/gl/gl_surface_egl.h @@ -14,6 +14,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "build/build_config.h" #include "ui/gfx/geometry/size.h" @@ -52,6 +53,8 @@ class GL_EXPORT GLSurfaceEGL : public GLSurface { // Implement GLSurface. EGLDisplay GetDisplay() override; + EGLConfig GetConfig() override; + GLSurface::Format GetFormat() override; static bool InitializeOneOff(); static EGLDisplay GetHardwareDisplay(); @@ -70,6 +73,9 @@ class GL_EXPORT GLSurfaceEGL : public GLSurface { protected: ~GLSurfaceEGL() override; + EGLConfig config_ = nullptr; + GLSurface::Format format_ = GLSurface::SURFACE_DEFAULT; + private: DISALLOW_COPY_AND_ASSIGN(GLSurfaceEGL); }; @@ -80,8 +86,8 @@ class GL_EXPORT NativeViewGLSurfaceEGL : public GLSurfaceEGL { explicit NativeViewGLSurfaceEGL(EGLNativeWindowType window); // Implement GLSurface. - EGLConfig GetConfig() override; - bool Initialize() override; + using GLSurfaceEGL::Initialize; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, @@ -102,6 +108,7 @@ class GL_EXPORT NativeViewGLSurfaceEGL : public GLSurfaceEGL { const Rect& bounds_rect, const RectF& crop_rect) override; bool FlipsVertically() const override; + bool BuffersFlipped() const override; // Create a NativeViewGLSurfaceEGL with an externally provided VSyncProvider. // Takes ownership of the VSyncProvider. @@ -114,9 +121,7 @@ class GL_EXPORT NativeViewGLSurfaceEGL : public GLSurfaceEGL { ~NativeViewGLSurfaceEGL() override; EGLNativeWindowType window_; - EGLConfig config_; gfx::Size size_; - bool alpha_; bool enable_fixed_size_angle_; void OnSetSwapInterval(int interval) override; @@ -154,8 +159,8 @@ class GL_EXPORT PbufferGLSurfaceEGL : public GLSurfaceEGL { explicit PbufferGLSurfaceEGL(const gfx::Size& size); // Implement GLSurface. - EGLConfig GetConfig() override; bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; @@ -184,8 +189,8 @@ class GL_EXPORT SurfacelessEGL : public GLSurfaceEGL { explicit SurfacelessEGL(const gfx::Size& size); // Implement GLSurface. - EGLConfig GetConfig() override; bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool IsOffscreen() override; bool IsSurfaceless() const override; diff --git a/chromium/ui/gl/gl_surface_glx.cc b/chromium/ui/gl/gl_surface_glx.cc index 20a117755c3..dea4bcc66fd 100644 --- a/chromium/ui/gl/gl_surface_glx.cc +++ b/chromium/ui/gl/gl_surface_glx.cc @@ -450,7 +450,7 @@ GLXDrawable NativeViewGLSurfaceGLX::GetDrawableHandle() const { return glx_window_; } -bool NativeViewGLSurfaceGLX::Initialize() { +bool NativeViewGLSurfaceGLX::Initialize(GLSurface::Format format) { XWindowAttributes attributes; if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) { LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_ @@ -588,7 +588,7 @@ UnmappedNativeViewGLSurfaceGLX::UnmappedNativeViewGLSurfaceGLX( size_.SetSize(1, 1); } -bool UnmappedNativeViewGLSurfaceGLX::Initialize() { +bool UnmappedNativeViewGLSurfaceGLX::Initialize(GLSurface::Format format) { DCHECK(!window_); gfx::AcceleratedWidget parent_window = diff --git a/chromium/ui/gl/gl_surface_glx.h b/chromium/ui/gl/gl_surface_glx.h index 294773cf778..03c127a4a8f 100644 --- a/chromium/ui/gl/gl_surface_glx.h +++ b/chromium/ui/gl/gl_surface_glx.h @@ -58,7 +58,7 @@ class GL_EXPORT NativeViewGLSurfaceGLX : public GLSurfaceGLX, explicit NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window); // Implement GLSurfaceGLX. - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, @@ -106,7 +106,7 @@ class GL_EXPORT UnmappedNativeViewGLSurfaceGLX : public GLSurfaceGLX { explicit UnmappedNativeViewGLSurfaceGLX(const gfx::Size& size); // Implement GLSurfaceGLX. - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; diff --git a/chromium/ui/gl/gl_surface_mac.cc b/chromium/ui/gl/gl_surface_mac.cc index c481462c774..7e7d5c9b8a2 100644 --- a/chromium/ui/gl/gl_surface_mac.cc +++ b/chromium/ui/gl/gl_surface_mac.cc @@ -29,7 +29,7 @@ class GL_EXPORT NoOpGLSurface : public GLSurface { explicit NoOpGLSurface(const gfx::Size& size) : size_(size) {} // Implement GLSurface. - bool Initialize() override { return true; } + bool Initialize(GLSurface::Format format) override { return true; } void Destroy() override {} bool IsOffscreen() override { return true; } gfx::SwapResult SwapBuffers() override { @@ -138,7 +138,7 @@ scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( switch (GetGLImplementation()) { case kGLImplementationOSMesaGL: { scoped_refptr<GLSurface> surface( - new GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, size)); + new GLSurfaceOSMesa(SURFACE_OSMESA_RGBA, size)); if (!surface->Initialize()) return NULL; diff --git a/chromium/ui/gl/gl_surface_osmesa.cc b/chromium/ui/gl/gl_surface_osmesa.cc index 6521bce4bd8..e39f9bd1a87 100644 --- a/chromium/ui/gl/gl_surface_osmesa.cc +++ b/chromium/ui/gl/gl_surface_osmesa.cc @@ -15,24 +15,17 @@ namespace gfx { -GLSurfaceOSMesa::GLSurfaceOSMesa(OSMesaSurfaceFormat format, +GLSurfaceOSMesa::GLSurfaceOSMesa(GLSurface::Format format, const gfx::Size& size) - : size_(size) { - switch (format) { - case OSMesaSurfaceFormatBGRA: - format_ = OSMESA_BGRA; - break; - case OSMesaSurfaceFormatRGBA: - format_ = OSMESA_RGBA; - break; - } + : size_(size), + format_(format) { // Implementations of OSMesa surface do not support having a 0 size. In such // cases use a (1, 1) surface. if (size_.GetArea() == 0) size_.SetSize(1, 1); } -bool GLSurfaceOSMesa::Initialize() { +bool GLSurfaceOSMesa::Initialize(GLSurface::Format format) { return Resize(size_, 1.f, true); } @@ -102,7 +95,7 @@ void* GLSurfaceOSMesa::GetHandle() { return buffer_.get(); } -unsigned GLSurfaceOSMesa::GetFormat() { +GLSurface::Format GLSurfaceOSMesa::GetFormat() { return format_; } @@ -117,7 +110,7 @@ gfx::SwapResult GLSurfaceOSMesaHeadless::SwapBuffers() { } GLSurfaceOSMesaHeadless::GLSurfaceOSMesaHeadless() - : GLSurfaceOSMesa(OSMesaSurfaceFormatBGRA, gfx::Size(1, 1)) { + : GLSurfaceOSMesa(SURFACE_OSMESA_BGRA, gfx::Size(1, 1)) { } GLSurfaceOSMesaHeadless::~GLSurfaceOSMesaHeadless() { Destroy(); } diff --git a/chromium/ui/gl/gl_surface_osmesa.h b/chromium/ui/gl/gl_surface_osmesa.h index dc5691660b1..d46a55c300e 100644 --- a/chromium/ui/gl/gl_surface_osmesa.h +++ b/chromium/ui/gl/gl_surface_osmesa.h @@ -14,17 +14,15 @@ namespace gfx { -enum OSMesaSurfaceFormat { OSMesaSurfaceFormatBGRA, OSMesaSurfaceFormatRGBA }; - // A surface that the Mesa software renderer draws to. This is actually just a // buffer in system memory. GetHandle returns a pointer to the buffer. These // surfaces can be resized and resizing preserves the contents. class GL_EXPORT GLSurfaceOSMesa : public GLSurface { public: - GLSurfaceOSMesa(OSMesaSurfaceFormat format, const gfx::Size& size); + GLSurfaceOSMesa(GLSurface::Format format, const gfx::Size& size); // Implement GLSurface. - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool Resize(const gfx::Size& new_size, float scale_factor, @@ -33,14 +31,14 @@ class GL_EXPORT GLSurfaceOSMesa : public GLSurface { gfx::SwapResult SwapBuffers() override; gfx::Size GetSize() override; void* GetHandle() override; - unsigned GetFormat() override; + GLSurface::Format GetFormat() override; protected: ~GLSurfaceOSMesa() override; private: - unsigned format_; gfx::Size size_; + GLSurface::Format format_; scoped_ptr<int32_t[]> buffer_; DISALLOW_COPY_AND_ASSIGN(GLSurfaceOSMesa); diff --git a/chromium/ui/gl/gl_surface_overlay.cc b/chromium/ui/gl/gl_surface_overlay.cc index e683c10fbd9..299e4a80d5a 100644 --- a/chromium/ui/gl/gl_surface_overlay.cc +++ b/chromium/ui/gl/gl_surface_overlay.cc @@ -23,6 +23,8 @@ GLSurfaceOverlay::GLSurfaceOverlay(int z_order, bounds_rect_(bounds_rect), crop_rect_(crop_rect) {} +GLSurfaceOverlay::GLSurfaceOverlay(const GLSurfaceOverlay& other) = default; + GLSurfaceOverlay::~GLSurfaceOverlay() {} bool GLSurfaceOverlay::ScheduleOverlayPlane(AcceleratedWidget widget) const { diff --git a/chromium/ui/gl/gl_surface_overlay.h b/chromium/ui/gl/gl_surface_overlay.h index df6923ab6db..240d90b8d27 100644 --- a/chromium/ui/gl/gl_surface_overlay.h +++ b/chromium/ui/gl/gl_surface_overlay.h @@ -23,6 +23,7 @@ class GLSurfaceOverlay { gl::GLImage* image, const Rect& bounds_rect, const RectF& crop_rect); + GLSurfaceOverlay(const GLSurfaceOverlay& other); ~GLSurfaceOverlay(); // Schedule the image as an overlay plane to be shown at swap time for diff --git a/chromium/ui/gl/gl_surface_ozone.cc b/chromium/ui/gl/gl_surface_ozone.cc index 67c1483ca9d..da803976543 100644 --- a/chromium/ui/gl/gl_surface_ozone.cc +++ b/chromium/ui/gl/gl_surface_ozone.cc @@ -16,6 +16,7 @@ #include "base/memory/weak_ptr.h" #include "base/threading/worker_pool.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gl/egl_util.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_image.h" #include "ui/gl/gl_image_ozone_native_pixmap.h" @@ -37,6 +38,32 @@ namespace gfx { namespace { +// Helper function for base::Bind to create callback to eglChooseConfig. +bool EglChooseConfig(EGLDisplay display, + const int32_t* attribs, + EGLConfig* configs, + int32_t config_size, + int32_t* num_configs) { + return eglChooseConfig(display, attribs, configs, config_size, num_configs); +} + +// Helper function for base::Bind to create callback to eglGetConfigAttrib. +bool EglGetConfigAttribute(EGLDisplay display, + EGLConfig config, + int32_t attribute, + int32_t* value) { + return eglGetConfigAttrib(display, config, attribute, value); +} + +// Populates EglConfigCallbacks with appropriate callbacks. +ui::EglConfigCallbacks GetEglConfigCallbacks(EGLDisplay display) { + ui::EglConfigCallbacks callbacks; + callbacks.choose_config = base::Bind(EglChooseConfig, display); + callbacks.get_config_attribute = base::Bind(EglGetConfigAttribute, display); + callbacks.get_last_error_string = base::Bind(&ui::GetLastEGLErrorString); + return callbacks; +} + void WaitForFence(EGLDisplay display, EGLSyncKHR fence) { eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR); @@ -49,7 +76,7 @@ class GL_EXPORT GLSurfaceOzoneEGL : public NativeViewGLSurfaceEGL { AcceleratedWidget widget); // GLSurface: - bool Initialize() override; + bool Initialize(gfx::GLSurface::Format format) override; bool Resize(const gfx::Size& size, float scale_factor, bool has_alpha) override; @@ -59,6 +86,7 @@ class GL_EXPORT GLSurfaceOzoneEGL : public NativeViewGLSurfaceEGL { GLImage* image, const Rect& bounds_rect, const RectF& crop_rect) override; + EGLConfig GetConfig() override; private: using NativeViewGLSurfaceEGL::Initialize; @@ -81,7 +109,8 @@ GLSurfaceOzoneEGL::GLSurfaceOzoneEGL( ozone_surface_(std::move(ozone_surface)), widget_(widget) {} -bool GLSurfaceOzoneEGL::Initialize() { +bool GLSurfaceOzoneEGL::Initialize(gfx::GLSurface::Format format) { + format_ = format; return Initialize(ozone_surface_->CreateVSyncProvider()); } @@ -115,6 +144,16 @@ bool GLSurfaceOzoneEGL::ScheduleOverlayPlane(int z_order, crop_rect); } +EGLConfig GLSurfaceOzoneEGL::GetConfig() { + if (!config_) { + ui::EglConfigCallbacks callbacks = GetEglConfigCallbacks(GetDisplay()); + config_ = ozone_surface_->GetEGLSurfaceConfig(callbacks); + } + if (config_) + return config_; + return NativeViewGLSurfaceEGL::GetConfig(); +} + GLSurfaceOzoneEGL::~GLSurfaceOzoneEGL() { Destroy(); // The EGL surface must be destroyed before SurfaceOzone. } @@ -137,7 +176,7 @@ bool GLSurfaceOzoneEGL::ReinitializeNativeSurface() { } window_ = ozone_surface_->GetNativeWindow(); - if (!Initialize()) { + if (!Initialize(format_)) { LOG(ERROR) << "Failed to initialize."; return false; } @@ -151,7 +190,7 @@ class GL_EXPORT GLSurfaceOzoneSurfaceless : public SurfacelessEGL { AcceleratedWidget widget); // GLSurface: - bool Initialize() override; + bool Initialize(gfx::GLSurface::Format format) override; bool Resize(const gfx::Size& size, float scale_factor, bool has_alpha) override; @@ -172,6 +211,7 @@ class GL_EXPORT GLSurfaceOzoneSurfaceless : public SurfacelessEGL { int width, int height, const SwapCompletionCallback& callback) override; + EGLConfig GetConfig() override; protected: struct PendingFrame { @@ -205,6 +245,7 @@ class GL_EXPORT GLSurfaceOzoneSurfaceless : public SurfacelessEGL { base::WeakPtrFactory<GLSurfaceOzoneSurfaceless> weak_factory_; + private: DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneSurfaceless); }; @@ -232,8 +273,8 @@ GLSurfaceOzoneSurfaceless::GLSurfaceOzoneSurfaceless( unsubmitted_frames_.push_back(new PendingFrame()); } -bool GLSurfaceOzoneSurfaceless::Initialize() { - if (!SurfacelessEGL::Initialize()) +bool GLSurfaceOzoneSurfaceless::Initialize(gfx::GLSurface::Format format) { + if (!SurfacelessEGL::Initialize(format)) return false; vsync_provider_ = ozone_surface_->CreateVSyncProvider(); if (!vsync_provider_) @@ -362,6 +403,16 @@ void GLSurfaceOzoneSurfaceless::PostSubBufferAsync( SwapBuffersAsync(callback); } +EGLConfig GLSurfaceOzoneSurfaceless::GetConfig() { + if (!config_) { + ui::EglConfigCallbacks callbacks = GetEglConfigCallbacks(GetDisplay()); + config_ = ozone_surface_->GetEGLSurfaceConfig(callbacks); + } + if (config_) + return config_; + return SurfacelessEGL::GetConfig(); +} + GLSurfaceOzoneSurfaceless::~GLSurfaceOzoneSurfaceless() { Destroy(); // The EGL surface must be destroyed before SurfaceOzone. } @@ -527,8 +578,16 @@ void GLSurfaceOzoneSurfacelessSurfaceImpl::Destroy() { if (!context_) return; scoped_refptr<gfx::GLContext> previous_context = gfx::GLContext::GetCurrent(); - scoped_refptr<gfx::GLSurface> previous_surface = gfx::GLSurface::GetCurrent(); - context_->MakeCurrent(this); + scoped_refptr<gfx::GLSurface> previous_surface; + + bool was_current = previous_context && previous_context->IsCurrent(nullptr) && + gfx::GLSurface::GetCurrent() == this; + if (!was_current) { + // Only take a reference to previous surface if it's not |this| + // because otherwise we can take a self reference from our own dtor. + previous_surface = gfx::GLSurface::GetCurrent(); + context_->MakeCurrent(this); + } glBindFramebufferEXT(GL_FRAMEBUFFER, 0); if (fbo_) { @@ -543,7 +602,7 @@ void GLSurfaceOzoneSurfacelessSurfaceImpl::Destroy() { image->Destroy(true); } - if (previous_context.get()) { + if (!was_current) { previous_context->MakeCurrent(previous_surface.get()); } else { context_->ReleaseCurrent(this); @@ -580,6 +639,9 @@ bool GLSurfaceOzoneSurfacelessSurfaceImpl::CreatePixmaps() { new GLImageOzoneNativePixmap(GetSize(), GL_BGRA_EXT); if (!image->Initialize(pixmap.get(), gfx::BufferFormat::BGRA_8888)) return false; + // Image must have Destroy() called before destruction. + if (images_[i]) + images_[i]->Destroy(true); images_[i] = image; // Bind image to texture. ScopedTextureBinder binder(GL_TEXTURE_2D, textures_[i]); @@ -691,7 +753,7 @@ scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( switch (GetGLImplementation()) { case kGLImplementationOSMesaGL: { scoped_refptr<GLSurface> surface( - new GLSurfaceOSMesa(OSMesaSurfaceFormatBGRA, size)); + new GLSurfaceOSMesa(SURFACE_OSMESA_BGRA, size)); if (!surface->Initialize()) return nullptr; @@ -702,8 +764,9 @@ scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() && (size.width() == 0 && size.height() == 0)) { surface = new SurfacelessEGL(size); - } else + } else { surface = new PbufferGLSurfaceEGL(size); + } if (!surface->Initialize()) return nullptr; diff --git a/chromium/ui/gl/gl_surface_stub.cc b/chromium/ui/gl/gl_surface_stub.cc index c49165fee1e..ad5b0b3fe45 100644 --- a/chromium/ui/gl/gl_surface_stub.cc +++ b/chromium/ui/gl/gl_surface_stub.cc @@ -9,6 +9,12 @@ namespace gfx { void GLSurfaceStub::Destroy() { } +bool GLSurfaceStub::Resize(const gfx::Size& size, + float scale_factor, + bool has_alpha) { + return true; +} + bool GLSurfaceStub::IsOffscreen() { return false; } @@ -25,6 +31,10 @@ void* GLSurfaceStub::GetHandle() { return NULL; } +bool GLSurfaceStub::BuffersFlipped() const { + return buffers_flipped_; +} + GLSurfaceStub::~GLSurfaceStub() {} } // namespace gfx diff --git a/chromium/ui/gl/gl_surface_stub.h b/chromium/ui/gl/gl_surface_stub.h index bf21573e1d5..9115b7c1dcd 100644 --- a/chromium/ui/gl/gl_surface_stub.h +++ b/chromium/ui/gl/gl_surface_stub.h @@ -13,19 +13,25 @@ namespace gfx { class GL_EXPORT GLSurfaceStub : public GLSurface { public: void SetSize(const gfx::Size& size) { size_ = size; } + void set_buffers_flipped(bool flipped) { buffers_flipped_ = flipped; } // Implement GLSurface. void Destroy() override; + bool Resize(const gfx::Size& size, + float scale_factor, + bool has_alpha) override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; gfx::Size GetSize() override; void* GetHandle() override; + bool BuffersFlipped() const override; protected: ~GLSurfaceStub() override; private: gfx::Size size_; + bool buffers_flipped_ = false; }; } // namespace gfx diff --git a/chromium/ui/gl/gl_surface_wgl.cc b/chromium/ui/gl/gl_surface_wgl.cc index 6495686f872..8890e5c7562 100644 --- a/chromium/ui/gl/gl_surface_wgl.cc +++ b/chromium/ui/gl/gl_surface_wgl.cc @@ -191,7 +191,7 @@ NativeViewGLSurfaceWGL::~NativeViewGLSurfaceWGL() { Destroy(); } -bool NativeViewGLSurfaceWGL::Initialize() { +bool NativeViewGLSurfaceWGL::Initialize(GLSurface::Format format) { DCHECK(!device_context_); RECT rect; @@ -305,7 +305,7 @@ PbufferGLSurfaceWGL::~PbufferGLSurfaceWGL() { Destroy(); } -bool PbufferGLSurfaceWGL::Initialize() { +bool PbufferGLSurfaceWGL::Initialize(GLSurface::Format format) { DCHECK(!device_context_); if (!gfx::g_driver_wgl.fn.wglCreatePbufferARBFn) { diff --git a/chromium/ui/gl/gl_surface_wgl.h b/chromium/ui/gl/gl_surface_wgl.h index 32c7f48a3ef..7466bcb0869 100644 --- a/chromium/ui/gl/gl_surface_wgl.h +++ b/chromium/ui/gl/gl_surface_wgl.h @@ -36,7 +36,7 @@ class NativeViewGLSurfaceWGL : public GLSurfaceWGL { explicit NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window); // Implement GLSurface. - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; @@ -60,7 +60,7 @@ class PbufferGLSurfaceWGL : public GLSurfaceWGL { explicit PbufferGLSurfaceWGL(const gfx::Size& size); // Implement GLSurface. - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; diff --git a/chromium/ui/gl/gl_surface_win.cc b/chromium/ui/gl/gl_surface_win.cc index 64a3011b9f0..9ac1f38a1a3 100644 --- a/chromium/ui/gl/gl_surface_win.cc +++ b/chromium/ui/gl/gl_surface_win.cc @@ -36,7 +36,7 @@ class NativeViewGLSurfaceOSMesa : public GLSurfaceOSMesa { explicit NativeViewGLSurfaceOSMesa(gfx::AcceleratedWidget window); // Implement subset of GLSurface. - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; @@ -55,6 +55,8 @@ class NativeViewGLSurfaceOSMesa : public GLSurfaceOSMesa { // Helper routine that does one-off initialization like determining the // pixel format. bool GLSurface::InitializeOneOffInternal() { + VSyncProviderWin::InitializeOneOff(); + switch (GetGLImplementation()) { case kGLImplementationDesktopGL: if (!GLSurfaceWGL::InitializeOneOff()) { @@ -81,7 +83,7 @@ bool GLSurface::InitializeOneOffInternal() { NativeViewGLSurfaceOSMesa::NativeViewGLSurfaceOSMesa( gfx::AcceleratedWidget window) - : GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, gfx::Size(1, 1)), + : GLSurfaceOSMesa(SURFACE_OSMESA_RGBA, gfx::Size(1, 1)), window_(window), device_context_(NULL) { DCHECK(window); @@ -91,8 +93,8 @@ NativeViewGLSurfaceOSMesa::~NativeViewGLSurfaceOSMesa() { Destroy(); } -bool NativeViewGLSurfaceOSMesa::Initialize() { - if (!GLSurfaceOSMesa::Initialize()) +bool NativeViewGLSurfaceOSMesa::Initialize(GLSurface::Format format) { + if (!GLSurfaceOSMesa::Initialize(format)) return false; device_context_ = GetDC(window_); @@ -236,7 +238,7 @@ scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( switch (GetGLImplementation()) { case kGLImplementationOSMesaGL: { scoped_refptr<GLSurface> surface( - new GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, size)); + new GLSurfaceOSMesa(SURFACE_OSMESA_RGBA, size)); if (!surface->Initialize()) return NULL; diff --git a/chromium/ui/gl/gl_surface_x11.cc b/chromium/ui/gl/gl_surface_x11.cc index 3a2a0da9fdd..4e36e2ee86e 100644 --- a/chromium/ui/gl/gl_surface_x11.cc +++ b/chromium/ui/gl/gl_surface_x11.cc @@ -32,7 +32,7 @@ class NativeViewGLSurfaceOSMesa : public GLSurfaceOSMesa { static bool InitializeOneOff(); // Implement a subset of GLSurface. - bool Initialize() override; + bool Initialize(GLSurface::Format format) override; void Destroy() override; bool Resize(const gfx::Size& new_size, float scale_factor, @@ -84,7 +84,7 @@ bool GLSurface::InitializeOneOffInternal() { NativeViewGLSurfaceOSMesa::NativeViewGLSurfaceOSMesa( gfx::AcceleratedWidget window) - : GLSurfaceOSMesa(OSMesaSurfaceFormatBGRA, gfx::Size(1, 1)), + : GLSurfaceOSMesa(SURFACE_OSMESA_BGRA, gfx::Size(1, 1)), xdisplay_(gfx::GetXDisplay()), window_graphics_context_(0), window_(window), @@ -109,8 +109,8 @@ bool NativeViewGLSurfaceOSMesa::InitializeOneOff() { return true; } -bool NativeViewGLSurfaceOSMesa::Initialize() { - if (!GLSurfaceOSMesa::Initialize()) +bool NativeViewGLSurfaceOSMesa::Initialize(GLSurface::Format format) { + if (!GLSurfaceOSMesa::Initialize(format)) return false; window_graphics_context_ = XCreateGC(xdisplay_, window_, 0, NULL); @@ -308,7 +308,7 @@ scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( switch (GetGLImplementation()) { case kGLImplementationOSMesaGL: { scoped_refptr<GLSurface> surface( - new GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, size)); + new GLSurfaceOSMesa(SURFACE_OSMESA_RGBA, size)); if (!surface->Initialize()) return NULL; diff --git a/chromium/ui/gl/gl_switches.cc b/chromium/ui/gl/gl_switches.cc index fd80ff4c85c..2411517d018 100644 --- a/chromium/ui/gl/gl_switches.cc +++ b/chromium/ui/gl/gl_switches.cc @@ -18,7 +18,6 @@ const char kGLImplementationMockName[] = "mock"; const char kANGLEImplementationDefaultName[] = "default"; const char kANGLEImplementationD3D9Name[] = "d3d9"; const char kANGLEImplementationD3D11Name[] = "d3d11"; -const char kANGLEImplementationWARPName[] = "warp"; const char kANGLEImplementationOpenGLName[] = "gl"; const char kANGLEImplementationOpenGLESName[] = "gles"; @@ -67,8 +66,8 @@ const char kSwiftShaderPath[] = "swiftshader-path"; // context will never be lost in any situations, say, a GPU reset. const char kGpuNoContextLost[] = "gpu-no-context-lost"; -// Turns on the use of DirectComposition to draw to the screen. -const char kUseDirectComposition[] = "use-direct-composition"; +// 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"; @@ -98,16 +97,16 @@ const char kDisableGLExtensions[] = "disable-gl-extensions"; // 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[] = { - kDisableGpuVsync, - kDisableD3D11, - kEnableGPUServiceLogging, - kEnableGPUServiceTracing, - kEnableUnsafeES3APIs, - kGpuNoContextLost, - kDisableGLDrawingForTests, - kOverrideUseGLWithOSMesaForTests, - kUseANGLE, - kUseDirectComposition, + kDisableGpuVsync, + kDisableD3D11, + kEnableGPUServiceLogging, + kEnableGPUServiceTracing, + kEnableUnsafeES3APIs, + kGpuNoContextLost, + kDisableGLDrawingForTests, + kOverrideUseGLWithOSMesaForTests, + kUseANGLE, + kDisableDirectComposition, }; const int kGLSwitchesCopiedFromGpuProcessHostNumSwitches = arraysize(kGLSwitchesCopiedFromGpuProcessHost); diff --git a/chromium/ui/gl/gl_switches.h b/chromium/ui/gl/gl_switches.h index 2c6122b217a..8153006bd8c 100644 --- a/chromium/ui/gl/gl_switches.h +++ b/chromium/ui/gl/gl_switches.h @@ -23,7 +23,6 @@ extern const char kGLImplementationMockName[]; GL_EXPORT extern const char kANGLEImplementationDefaultName[]; GL_EXPORT extern const char kANGLEImplementationD3D9Name[]; GL_EXPORT extern const char kANGLEImplementationD3D11Name[]; -GL_EXPORT extern const char kANGLEImplementationWARPName[]; GL_EXPORT extern const char kANGLEImplementationOpenGLName[]; GL_EXPORT extern const char kANGLEImplementationOpenGLESName[]; @@ -36,7 +35,7 @@ GL_EXPORT extern const char kDisableGpuVsync[]; GL_EXPORT extern const char kEnableGPUServiceLogging[]; GL_EXPORT extern const char kEnableGPUServiceTracing[]; GL_EXPORT extern const char kGpuNoContextLost[]; -GL_EXPORT extern const char kUseDirectComposition[]; +GL_EXPORT extern const char kDisableDirectComposition[]; GL_EXPORT extern const char kSupportsDualGpus[]; diff --git a/chromium/ui/gl/gl_unittests_apk.isolate b/chromium/ui/gl/gl_unittests_apk.isolate index 1032ef6c2c1..03c9847ec41 100644 --- a/chromium/ui/gl/gl_unittests_apk.isolate +++ b/chromium/ui/gl/gl_unittests_apk.isolate @@ -8,6 +8,7 @@ 'variables': { 'command': [ '<(PRODUCT_DIR)/bin/run_gl_unittests', + '--logcat-output-dir', '${ISOLATED_OUTDIR}/logcats', ], 'files': [ '<(PRODUCT_DIR)/bin/run_gl_unittests', diff --git a/chromium/ui/gl/gl_version_info.cc b/chromium/ui/gl/gl_version_info.cc index cc960b0a3a8..42616509e54 100644 --- a/chromium/ui/gl/gl_version_info.cc +++ b/chromium/ui/gl/gl_version_info.cc @@ -83,6 +83,7 @@ void GLVersionInfo::ParseVersionString(const char* version_str, } if (*is_es && *major_version == 3) *is_es3 = true; + DCHECK(major_version != 0); } } // namespace gfx diff --git a/chromium/ui/gl/gpu_switching_manager.h b/chromium/ui/gl/gpu_switching_manager.h index 7849d4acb71..92e865af7e3 100644 --- a/chromium/ui/gl/gpu_switching_manager.h +++ b/chromium/ui/gl/gpu_switching_manager.h @@ -10,6 +10,7 @@ #include <vector> #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/observer_list.h" #include "build/build_config.h" diff --git a/chromium/ui/gl/gpu_timing.cc b/chromium/ui/gl/gpu_timing.cc index 8989bd55b2a..f5d9ae0da4e 100644 --- a/chromium/ui/gl/gpu_timing.cc +++ b/chromium/ui/gl/gpu_timing.cc @@ -20,6 +20,12 @@ int64_t NanoToMicro(uint64_t nano_seconds) { return static_cast<int64_t>(up / base::Time::kNanosecondsPerMicrosecond); } +int32_t QueryTimestampBits() { + GLint timestamp_bits; + glGetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, ×tamp_bits); + return static_cast<int32_t>(timestamp_bits); +} + class GPUTimingImpl : public GPUTiming { public: GPUTimingImpl(GLContextReal* context); @@ -73,6 +79,7 @@ class GPUTimingImpl : public GPUTiming { int64_t offset_ = 0; // offset cache when timer_type_ == kTimerTypeARB bool offset_valid_ = false; bool force_time_elapsed_query_ = false; + int32_t timestamp_bit_count_gl_ = -1; // gl implementation timestamp bits uint32_t next_timer_query_id_ = 0; uint32_t next_good_timer_query_id_ = 0; // identify bad ids for disjoints. @@ -310,14 +317,21 @@ GPUTimingImpl::GPUTimingImpl(GLContextReal* context) { DCHECK(context); const GLVersionInfo* version_info = context->GetVersionInfo(); DCHECK(version_info); - if (version_info->is_es3 && // glGetInteger64v is supported under ES3. - context->HasExtension("GL_EXT_disjoint_timer_query")) { + if (context->HasExtension("GL_EXT_disjoint_timer_query")) { timer_type_ = GPUTiming::kTimerTypeDisjoint; } else if (context->HasExtension("GL_ARB_timer_query")) { timer_type_ = GPUTiming::kTimerTypeARB; } else if (context->HasExtension("GL_EXT_timer_query")) { timer_type_ = GPUTiming::kTimerTypeEXT; force_time_elapsed_query_ = true; + timestamp_bit_count_gl_ = 0; + } + // The command glGetInteger64v is only supported under ES3 and GL3.2. Since it + // is only used for timestamps, we workaround this by emulating timestamps + // so WebGL 1.0 will still have access to the extension. + if (!version_info->IsAtLeastGLES(3, 0) && !version_info->IsAtLeastGL(3, 2)) { + force_time_elapsed_query_ = true; + timestamp_bit_count_gl_ = 0; } } @@ -342,9 +356,19 @@ int64_t GPUTimingImpl::CalculateTimerOffset() { timer_type_ == GPUTiming::kTimerTypeARB) { GLint64 gl_now = 0; glGetInteger64v(GL_TIMESTAMP, &gl_now); - int64_t micro_now = NanoToMicro(gl_now); - offset_ = GetCurrentCPUTime() - micro_now; - offset_valid_ = (timer_type_ == GPUTiming::kTimerTypeARB); + const int64_t cpu_time = GetCurrentCPUTime(); + const int64_t micro_offset = cpu_time - NanoToMicro(gl_now); + + // We cannot expect these instructions to run with the accuracy + // within 1 microsecond, instead discard differences which are less + // than a single millisecond. + base::TimeDelta delta = + base::TimeDelta::FromMicroseconds(micro_offset - offset_); + + if (delta.magnitude().InMilliseconds() >= 1) { + offset_ = micro_offset; + offset_valid_ = (timer_type_ == GPUTiming::kTimerTypeARB); + } } else { offset_ = 0; offset_valid_ = true; @@ -382,6 +406,15 @@ void GPUTimingImpl::EndElapsedTimeQuery(scoped_refptr<QueryResult> result) { scoped_refptr<QueryResult> GPUTimingImpl::DoTimeStampQuery() { DCHECK(timer_type_ != GPUTiming::kTimerTypeInvalid); + // Certain GL drivers have timestamp bit count set to 0 which means timestamps + // aren't supported. Emulate them with time elapsed queries if that is the + // case. + if (timestamp_bit_count_gl_ == -1) { + DCHECK(timer_type_ != GPUTiming::kTimerTypeEXT); + timestamp_bit_count_gl_ = QueryTimestampBits(); + force_time_elapsed_query_ = (timestamp_bit_count_gl_ == 0); + } + if (force_time_elapsed_query_) { // Replace with elapsed timer queries instead. scoped_refptr<QueryResult> result = BeginElapsedTimeQuery(); diff --git a/chromium/ui/gl/gpu_timing_fake.cc b/chromium/ui/gl/gpu_timing_fake.cc index 03f4af3d6ea..bce0bf1acad 100644 --- a/chromium/ui/gl/gpu_timing_fake.cc +++ b/chromium/ui/gl/gpu_timing_fake.cc @@ -16,6 +16,9 @@ using ::testing::AtMost; using ::testing::Exactly; using ::testing::Invoke; using ::testing::NotNull; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; int64_t GPUTimingFake::fake_cpu_time_ = 0; @@ -75,11 +78,13 @@ void GPUTimingFake::ExpectNoDisjointCalls(MockGLInterface& gl) { EXPECT_CALL(gl, GetIntegerv(GL_GPU_DISJOINT_EXT, _)).Times(Exactly(0)); } -void GPUTimingFake::ExpectGPUTimeStampQuery( - MockGLInterface& gl, bool elapsed_query) { +void GPUTimingFake::ExpectGPUTimeStampQuery(MockGLInterface& gl, + bool elapsed_query) { EXPECT_CALL(gl, GenQueries(1, NotNull())).Times(Exactly(1)) .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLGenQueries)); + EXPECT_CALL(gl, GetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<2>(64), Return())); if (!elapsed_query) { // Time Stamp based queries. EXPECT_CALL(gl, GetInteger64v(GL_TIMESTAMP, _)) @@ -121,6 +126,9 @@ void GPUTimingFake::ExpectGPUTimerQuery( if (!elapsed_query) { // Time Stamp based queries. + EXPECT_CALL(gl, GetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<2>(64), Return())); + EXPECT_CALL(gl, GetInteger64v(GL_TIMESTAMP, _)) .WillRepeatedly( Invoke(this, &GPUTimingFake::FakeGLGetInteger64v)); diff --git a/chromium/ui/gl/gpu_timing_unittest.cc b/chromium/ui/gl/gpu_timing_unittest.cc index 222fa55612b..22f6562e43b 100644 --- a/chromium/ui/gl/gpu_timing_unittest.cc +++ b/chromium/ui/gl/gpu_timing_unittest.cc @@ -17,6 +17,12 @@ namespace gfx { +using ::testing::Exactly; +using ::testing::NotNull; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; + class GPUTimingTest : public testing::Test { public: void SetUp() override { @@ -87,6 +93,7 @@ TEST_F(GPUTimingTest, FakeTimerTest) { TEST_F(GPUTimingTest, ForceTimeElapsedQuery) { // Test that forcing time elapsed query affects all clients. + SetupGLContext("3.2", "GL_ARB_timer_query"); scoped_refptr<GPUTimingClient> client1 = CreateGPUTimingClient(); EXPECT_FALSE(client1->IsForceTimeElapsedQuery()); @@ -106,7 +113,7 @@ TEST_F(GPUTimingTest, QueryTimeStampTest) { scoped_refptr<GPUTimingClient> client = CreateGPUTimingClient(); scoped_ptr<GPUTimer> gpu_timer = client->CreateGPUTimer(false); - const int64_t begin_cpu_time = 123; + const int64_t begin_cpu_time = 1230; const int64_t begin_gl_time = 10 * base::Time::kNanosecondsPerMicrosecond; const int64_t cpu_gl_offset = begin_gl_time / base::Time::kNanosecondsPerMicrosecond - begin_cpu_time; @@ -163,4 +170,41 @@ TEST_F(GPUTimingTest, QueryTimeStampUsingElapsedTest) { EXPECT_EQ(begin_cpu_time, end); } +TEST_F(GPUTimingTest, QueryTimestampUsingElapsedARBTest) { + // Test timestamp queries on platforms with GL_ARB_timer_query but still lack + // support for timestamp queries + SetupGLContext("3.2", "GL_ARB_timer_query"); + scoped_refptr<GPUTimingClient> client = CreateGPUTimingClient(); + scoped_ptr<GPUTimer> gpu_timer = client->CreateGPUTimer(false); + + const int64_t begin_cpu_time = 123; + const int64_t begin_gl_time = 10 * base::Time::kNanosecondsPerMicrosecond; + const int64_t cpu_gl_offset = begin_gl_time - begin_cpu_time; + gpu_timing_fake_queries_.SetCPUGLOffset(cpu_gl_offset); + gpu_timing_fake_queries_.SetCurrentCPUTime(begin_cpu_time); + + gpu_timing_fake_queries_.ExpectGPUTimeStampQuery(*gl_, true); + + // Custom mock override to ensure the timestamp bits are 0 + EXPECT_CALL(*gl_, GetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, NotNull())) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<2>(0), Return())); + + gpu_timer->QueryTimeStamp(); + + gpu_timing_fake_queries_.SetCurrentCPUTime(begin_cpu_time - 1); + EXPECT_FALSE(gpu_timer->IsAvailable()); + + gpu_timing_fake_queries_.SetCurrentCPUTime(begin_cpu_time + 1); + EXPECT_TRUE(gpu_timer->IsAvailable()); + EXPECT_EQ(0, gpu_timer->GetDeltaElapsed()); + + int64_t start, end; + gpu_timer->GetStartEndTimestamps(&start, &end); + // Force time elapsed won't be set until a query is actually attempted + ASSERT_TRUE(client->IsForceTimeElapsedQuery()); + EXPECT_EQ(begin_cpu_time, start); + EXPECT_EQ(begin_cpu_time, end); +} + } // namespace gpu diff --git a/chromium/ui/gl/vsync_provider_win.cc b/chromium/ui/gl/vsync_provider_win.cc index 2152af0f917..ab76a966c64 100644 --- a/chromium/ui/gl/vsync_provider_win.cc +++ b/chromium/ui/gl/vsync_provider_win.cc @@ -13,13 +13,30 @@ namespace gfx { +namespace { +bool g_use_dwm_vsync; +} // namespace + VSyncProviderWin::VSyncProviderWin(gfx::AcceleratedWidget window) : window_(window) { - use_dwm_ = (base::win::GetVersion() >= base::win::VERSION_WIN7); } VSyncProviderWin::~VSyncProviderWin() {} +// static +void VSyncProviderWin::InitializeOneOff() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + g_use_dwm_vsync = (base::win::GetVersion() >= base::win::VERSION_WIN7); + + if (g_use_dwm_vsync) { + // Prewarm sandbox + ::LoadLibrary(L"dwmapi.dll"); + } +} + void VSyncProviderWin::GetVSyncParameters(const UpdateVSyncCallback& callback) { TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters"); @@ -29,7 +46,7 @@ void VSyncProviderWin::GetVSyncParameters(const UpdateVSyncCallback& callback) { // Query the DWM timing info first if available. This will provide the most // precise values. - if (use_dwm_) { + if (g_use_dwm_vsync) { DWM_TIMING_INFO timing_info; timing_info.cbSize = sizeof(timing_info); HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info); diff --git a/chromium/ui/gl/vsync_provider_win.h b/chromium/ui/gl/vsync_provider_win.h index f54bad43793..6b7e64cadc5 100644 --- a/chromium/ui/gl/vsync_provider_win.h +++ b/chromium/ui/gl/vsync_provider_win.h @@ -13,12 +13,13 @@ class GL_EXPORT VSyncProviderWin : public VSyncProvider { explicit VSyncProviderWin(gfx::AcceleratedWidget window); ~VSyncProviderWin() override; + static void InitializeOneOff(); + // VSyncProvider overrides; void GetVSyncParameters(const UpdateVSyncCallback& callback) override; private: gfx::AcceleratedWidget window_; - bool use_dwm_; DISALLOW_COPY_AND_ASSIGN(VSyncProviderWin); }; diff --git a/chromium/ui/keyboard/BUILD.gn b/chromium/ui/keyboard/BUILD.gn index 61eb9514eff..ae966b8b448 100644 --- a/chromium/ui/keyboard/BUILD.gn +++ b/chromium/ui/keyboard/BUILD.gn @@ -2,6 +2,7 @@ # 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") import("//testing/test.gni") import("//third_party/google_input_tools/closure.gni") import("//third_party/google_input_tools/inputview.gni") @@ -126,6 +127,15 @@ build_closure("inputview") { path = rebase_path("//third_party/google_input_tools") } +mojom("mojom") { + sources = [ + "keyboard.mojom", + ] + deps = [ + "//ui/mojo/geometry:interfaces", + ] +} + test("keyboard_unittests") { sources = [ "keyboard_controller_unittest.cc", @@ -136,7 +146,6 @@ test("keyboard_unittests") { deps = [ ":keyboard", "//base", - "//base/allocator", "//base/test:test_support", "//skia", "//testing/gtest", diff --git a/chromium/ui/keyboard/content/keyboard_ui_content.cc b/chromium/ui/keyboard/content/keyboard_ui_content.cc index f117e781b03..7f97525348f 100644 --- a/chromium/ui/keyboard/content/keyboard_ui_content.cc +++ b/chromium/ui/keyboard/content/keyboard_ui_content.cc @@ -201,7 +201,7 @@ aura::Window* KeyboardUIContent::GetKeyboardWindow() { } bool KeyboardUIContent::HasKeyboardWindow() const { - return keyboard_contents_; + return !!keyboard_contents_; } bool KeyboardUIContent::ShouldWindowOverscroll(aura::Window* window) const { diff --git a/chromium/ui/keyboard/keyboard.gyp b/chromium/ui/keyboard/keyboard.gyp index 96fc1fdfa67..38d12a008a4 100644 --- a/chromium/ui/keyboard/keyboard.gyp +++ b/chromium/ui/keyboard/keyboard.gyp @@ -149,21 +149,6 @@ 'keyboard_util_unittest.cc', 'test/run_all_unittests.cc', ], - 'conditions': [ - ['OS=="linux" and use_allocator!="none"', { - 'dependencies': [ - '<(DEPTH)/base/allocator/allocator.gyp:allocator', - ], - 'link_settings': { - 'ldflags': ['-rdynamic'], - }, - }], - ['OS=="win" and win_use_allocator_shim==1', { - 'dependencies': [ - '<(DEPTH)/base/allocator/allocator.gyp:allocator', - ], - }], - ], }, ], } diff --git a/chromium/ui/keyboard/keyboard.mojom b/chromium/ui/keyboard/keyboard.mojom new file mode 100644 index 00000000000..212765b17a5 --- /dev/null +++ b/chromium/ui/keyboard/keyboard.mojom @@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module keyboard.mojom; + +import "ui/mojo/geometry/geometry.mojom"; + +interface KeyboardObserver { + // Sent any time state changes in the keyboard. + OnKeyboardStateChanged(bool is_enabled, + bool is_visible, + uint64 display_id, + mojo.Rect bounds); +}; + +interface Keyboard { + // TODO(sky): needs display id. + Show(); + Hide(); + + // Adds an observer. OnKeyboardStateChanged() is immediately called to give + // the initial state. + AddObserver(KeyboardObserver observer); +}; diff --git a/chromium/ui/keyboard/keyboard_controller.cc b/chromium/ui/keyboard/keyboard_controller.cc index 9eeb34789b0..ad1e640afc9 100644 --- a/chromium/ui/keyboard/keyboard_controller.cc +++ b/chromium/ui/keyboard/keyboard_controller.cc @@ -388,7 +388,8 @@ void KeyboardController::OnInputMethodDestroyed( } void KeyboardController::OnShowImeIfNeeded() { - ShowKeyboardInternal(); + if (IsKeyboardEnabled()) + ShowKeyboardInternal(); } void KeyboardController::ShowKeyboardInternal() { diff --git a/chromium/ui/keyboard/keyboard_controller.h b/chromium/ui/keyboard/keyboard_controller.h index ecd991d265f..3c0765e57f9 100644 --- a/chromium/ui/keyboard/keyboard_controller.h +++ b/chromium/ui/keyboard/keyboard_controller.h @@ -65,9 +65,7 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, aura::Window* GetContainerWindow(); // Whether the container window for the keyboard has been initialized. - bool keyboard_container_initialized() const { - return container_.get() != NULL; - } + bool keyboard_container_initialized() const { return container_ != nullptr; } // Reloads the content of the keyboard. No-op if the keyboard content is not // loaded yet. diff --git a/chromium/ui/keyboard/keyboard_controller_unittest.cc b/chromium/ui/keyboard/keyboard_controller_unittest.cc index ddcf2e0562a..63bbeacb200 100644 --- a/chromium/ui/keyboard/keyboard_controller_unittest.cc +++ b/chromium/ui/keyboard/keyboard_controller_unittest.cc @@ -96,7 +96,7 @@ class TestKeyboardUI : public KeyboardUI { } // Overridden from KeyboardUI: - bool HasKeyboardWindow() const override { return window_; } + bool HasKeyboardWindow() const override { return !!window_; } bool ShouldWindowOverscroll(aura::Window* window) const override { return true; } @@ -316,6 +316,7 @@ TEST_F(KeyboardControllerTest, FloatingKeyboardSize) { // Tests that tapping/clicking inside the keyboard does not give it focus. TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) { + keyboard::SetAccessibilityKeyboardEnabled(true); const gfx::Rect& root_bounds = root_window()->bounds(); aura::test::EventCountDelegate delegate; scoped_ptr<aura::Window> window(new aura::Window(&delegate)); @@ -356,9 +357,11 @@ TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) { generator.ClickLeftButton(); EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset()); keyboard_container->RemovePreTargetHandler(&observer); + keyboard::SetAccessibilityKeyboardEnabled(false); } TEST_F(KeyboardControllerTest, VisibilityChangeWithTextInputTypeChange) { + keyboard::SetAccessibilityKeyboardEnabled(true); ui::DummyTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT); ui::DummyTextInputClient input_client_1(ui::TEXT_INPUT_TYPE_TEXT); ui::DummyTextInputClient input_client_2(ui::TEXT_INPUT_TYPE_TEXT); @@ -394,6 +397,7 @@ TEST_F(KeyboardControllerTest, VisibilityChangeWithTextInputTypeChange) { EXPECT_FALSE(WillHideKeyboard()); EXPECT_TRUE(keyboard_container->IsVisible()); + keyboard::SetAccessibilityKeyboardEnabled(false); } // Test to prevent spurious overscroll boxes when changing tabs during keyboard @@ -423,6 +427,7 @@ TEST_F(KeyboardControllerTest, CheckOverscrollInsetDuringVisibilityChange) { // Verify switch to FLOATING mode will reset the overscroll or resize and when // in FLOATING mode, overscroll or resize wont be triggered. TEST_F(KeyboardControllerTest, FloatingKeyboardDontOverscrollOrResize) { + keyboard::SetAccessibilityKeyboardEnabled(true); ui::DummyTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT); ui::DummyTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE); @@ -453,6 +458,7 @@ TEST_F(KeyboardControllerTest, FloatingKeyboardDontOverscrollOrResize) { // In FLOATING mode, no overscroll or resize should be triggered. EXPECT_EQ(3, number_of_calls()); EXPECT_EQ(gfx::Rect(), controller()->current_keyboard_bounds()); + keyboard::SetAccessibilityKeyboardEnabled(false); } // Verify switch to FULL_WIDTH mode will move virtual keyboard to the right @@ -479,6 +485,7 @@ TEST_F(KeyboardControllerTest, SwitchToFullWidthVirtualKeyboard) { } TEST_F(KeyboardControllerTest, AlwaysVisibleWhenLocked) { + keyboard::SetAccessibilityKeyboardEnabled(true); ui::DummyTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT); ui::DummyTextInputClient input_client_1(ui::TEXT_INPUT_TYPE_TEXT); ui::DummyTextInputClient no_input_client_0(ui::TEXT_INPUT_TYPE_NONE); @@ -514,6 +521,7 @@ TEST_F(KeyboardControllerTest, AlwaysVisibleWhenLocked) { // Wait for hide keyboard to finish. base::MessageLoop::current()->Run(); EXPECT_FALSE(keyboard_container->IsVisible()); + keyboard::SetAccessibilityKeyboardEnabled(false); } class KeyboardControllerAnimationTest : public KeyboardControllerTest { @@ -553,6 +561,7 @@ class KeyboardControllerAnimationTest : public KeyboardControllerTest { // Tests virtual keyboard has correct show and hide animation. TEST_F(KeyboardControllerAnimationTest, ContainerAnimation) { ui::Layer* layer = keyboard_container()->layer(); + keyboard::SetAccessibilityKeyboardEnabled(true); ShowKeyboard(); // Keyboard container and window should immediately become visible before @@ -593,12 +602,14 @@ TEST_F(KeyboardControllerAnimationTest, ContainerAnimation) { EXPECT_GT(hide_start_opacity, hide_end_opacity); EXPECT_EQ(transform, layer->transform()); EXPECT_EQ(gfx::Rect(), notified_bounds()); + keyboard::SetAccessibilityKeyboardEnabled(false); } // Show keyboard during keyboard hide animation should abort the hide animation // and the keyboard should animate in. // Test for crbug.com/333284. TEST_F(KeyboardControllerAnimationTest, ContainerShowWhileHide) { + keyboard::SetAccessibilityKeyboardEnabled(true); ui::Layer* layer = keyboard_container()->layer(); ShowKeyboard(); RunAnimationForLayer(layer); @@ -611,6 +622,7 @@ TEST_F(KeyboardControllerAnimationTest, ContainerShowWhileHide) { EXPECT_TRUE(keyboard_window()->IsVisible()); EXPECT_EQ(1.0, layer->opacity()); EXPECT_EQ(gfx::Transform(), layer->transform()); + keyboard::SetAccessibilityKeyboardEnabled(false); } // Test for crbug.com/568274. diff --git a/chromium/ui/keyboard/resources/OWNERS b/chromium/ui/keyboard/resources/OWNERS index 7a6ff670440..d1ba0b0bf9b 100644 --- a/chromium/ui/keyboard/resources/OWNERS +++ b/chromium/ui/keyboard/resources/OWNERS @@ -1,3 +1,2 @@ -bryeung@chromium.org bshe@chromium.org -kevers@chromium.org +rsadam@chromium.org diff --git a/chromium/ui/keyboard/resources/inputview_adapter.js b/chromium/ui/keyboard/resources/inputview_adapter.js index 1e0d459f974..32849e0163c 100644 --- a/chromium/ui/keyboard/resources/inputview_adapter.js +++ b/chromium/ui/keyboard/resources/inputview_adapter.js @@ -224,6 +224,16 @@ function registerInputviewApi() { } /** + * Retrieve the current display size in inches. + * @param {function} callback + * @private + */ + function getDisplayInInches_(callback) { + callback(0); + } + + + /** * Retrieve the current input method configuration. * @param {function} callback The callback function for processing the * name of the active input mehtod. @@ -320,6 +330,7 @@ function registerInputviewApi() { getCurrentInputMethod: getCurrentInputMethod_, getInputMethodConfig: getInputMethodConfig_, switchToInputMethod: switchToInputMethod_, + getDisplayInInches: getDisplayInInches_, openSettings: openSettings_ }; diff --git a/chromium/ui/login/account_picker/user_pod_row.css b/chromium/ui/login/account_picker/user_pod_row.css index 412179398c7..88f117b5b9a 100644 --- a/chromium/ui/login/account_picker/user_pod_row.css +++ b/chromium/ui/login/account_picker/user_pod_row.css @@ -502,7 +502,7 @@ html[dir=rtl] .user-type-icon-area { } .action-box-area.menu-moved-up { - -webkit-transform: rotate(180deg); + transform: rotate(180deg); } html[dir=rtl] .action-box-area.active ~ .action-box-menu { diff --git a/chromium/ui/login/account_picker/user_pod_row.js b/chromium/ui/login/account_picker/user_pod_row.js index c36496d08a4..8bc93b994df 100644 --- a/chromium/ui/login/account_picker/user_pod_row.js +++ b/chromium/ui/login/account_picker/user_pod_row.js @@ -3002,21 +3002,23 @@ cr.define('login', function() { if (this.pods.length == 1) return null; - // The desktop User Manager can send the index of a pod that should be - // initially focused in url hash. - var podIndex = parseInt(window.location.hash.substr(1)); - if (isNaN(podIndex) || podIndex >= this.pods.length) - return null; - return this.pods[podIndex]; + // The desktop User Manager can send an URI encoded profile path in the + // url hash, that indicates a pod that should be initially focused. + var focusedProfilePath = + decodeURIComponent(window.location.hash.substr(1)); + for (var i = 0, pod; pod = this.pods[i]; ++i) { + if (focusedProfilePath === pod.user.profilePath) + return pod; + } + return null; } var lockedPod = this.lockedPod; if (lockedPod) return lockedPod; - for (var i = 0, pod; pod = this.pods[i]; ++i) { - if (!pod.multiProfilesPolicyApplied) { + for (i = 0; pod = this.pods[i]; ++i) { + if (!pod.multiProfilesPolicyApplied) return pod; - } } return this.pods[0]; }, @@ -3243,14 +3245,18 @@ cr.define('login', function() { * Called right after the pod row is shown. */ handleAfterShow: function() { + var focusedPod = this.focusedPod_; + // Without timeout changes in pods positions will be animated even though // it happened when 'flying-pods' class was disabled. setTimeout(function() { Oobe.getInstance().toggleClass('flying-pods', true); + if (focusedPod) + ensureTransitionEndEvent(focusedPod); }, 0); + // Force input focus for user pod on show and once transition ends. - if (this.focusedPod_) { - var focusedPod = this.focusedPod_; + if (focusedPod) { var screen = this.parentNode; var self = this; focusedPod.addEventListener('webkitTransitionEnd', function f(e) { @@ -3259,8 +3265,6 @@ cr.define('login', function() { // Notify screen that it is ready. screen.onShow(); }); - // Guard timer for 1 second -- it would conver all possible animations. - ensureTransitionEndEvent(focusedPod, 1000); } }, diff --git a/chromium/ui/login/display_manager.js b/chromium/ui/login/display_manager.js index a945bfef1ca..b8c6b1d27f2 100644 --- a/chromium/ui/login/display_manager.js +++ b/chromium/ui/login/display_manager.js @@ -31,6 +31,8 @@ /** @const */ var SCREEN_TERMS_OF_SERVICE = 'terms-of-service'; /** @const */ var SCREEN_WRONG_HWID = 'wrong-hwid'; /** @const */ var SCREEN_DEVICE_DISABLED = 'device-disabled'; +/** @const */ var SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR = + 'unrecoverable-cryptohome-error'; /* Accelerator identifiers. Must be kept in sync with webui_login_view.cc. */ /** @const */ var ACCELERATOR_CANCEL = 'cancel'; diff --git a/chromium/ui/login/screen_container.css b/chromium/ui/login/screen_container.css index 97a824b8baa..57742eaf678 100644 --- a/chromium/ui/login/screen_container.css +++ b/chromium/ui/login/screen_container.css @@ -70,7 +70,8 @@ #oobe.terms-of-service #inner-container, #oobe.update #inner-container, #oobe.user-image #inner-container, -#oobe.wrong-hwid #inner-container { +#oobe.wrong-hwid #inner-container, +#oobe.unrecoverable-cryptohome-error #inner-container { background: white; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3), 0 4px 23px 5px rgba(0, 0, 0, 0.2), @@ -87,11 +88,11 @@ /* Only play this animation when 'down' class is removed. */ .oobe-display #inner-container:not(.down) { - -webkit-transition: -webkit-transform 200ms ease-in-out; + -webkit-transition: transform 200ms ease-in-out; } .oobe-display #inner-container.down { - -webkit-transform: translateY(50px) rotateX(-2.5deg); + transform: translateY(50px) rotateX(-2.5deg); } #step-logo { @@ -147,7 +148,8 @@ #supervised-user-creation-dot, #terms-of-service-dot, #tpm-error-message-dot, -#wrong-hwid-dot { +#wrong-hwid-dot, +#unrecoverable-cryptohome-error-dot { display: none; } diff --git a/chromium/ui/message_center/BUILD.gn b/chromium/ui/message_center/BUILD.gn index bda992c3c37..b925476379f 100644 --- a/chromium/ui/message_center/BUILD.gn +++ b/chromium/ui/message_center/BUILD.gn @@ -6,6 +6,7 @@ import("//build/config/features.gni") import("//build/config/ui.gni") import("//testing/test.gni") +# TODO(msw|mukai|dewittj): Move ash-specific files: crbug.com/585175 component("message_center") { deps = [ "//base", @@ -73,6 +74,8 @@ component("message_center") { "notifier_settings.h", "popup_timer.cc", "popup_timer.h", + "popup_timers_controller.cc", + "popup_timers_controller.h", ] if (is_win) { @@ -88,6 +91,10 @@ 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", @@ -124,15 +131,6 @@ component("message_center") { "//ui/views", ] } - - if (use_ash) { - sources += [ - "views/message_bubble_base.cc", - "views/message_bubble_base.h", - "views/message_center_bubble.cc", - "views/message_center_bubble.h", - ] - } } else { # Notification service disabled. sources = [ @@ -190,7 +188,6 @@ test("message_center_unittests") { ":message_center", ":test_support", "//base", - "//base/allocator", "//base/test:test_support", "//skia", "//testing/gtest", @@ -205,6 +202,10 @@ test("message_center_unittests") { "//url", ] + data_deps = [ + "//ui/resources:ui_test_pak_data", + ] + if (enable_notifications && !is_android) { sources += [ "cocoa/notification_controller_unittest.mm", diff --git a/chromium/ui/message_center/cocoa/settings_controller_unittest.mm b/chromium/ui/message_center/cocoa/settings_controller_unittest.mm index c686fcfe6b2..a1291b7d925 100644 --- a/chromium/ui/message_center/cocoa/settings_controller_unittest.mm +++ b/chromium/ui/message_center/cocoa/settings_controller_unittest.mm @@ -37,8 +37,7 @@ NotifierGroup* NewGroup(const std::string& name, const std::string& login_info) { return new NotifierGroup(gfx::Image(), base::UTF8ToUTF16(name), - base::UTF8ToUTF16(login_info), - true); + base::UTF8ToUTF16(login_info)); } Notifier* NewNotifier(const std::string& id, diff --git a/chromium/ui/message_center/fake_message_center.cc b/chromium/ui/message_center/fake_message_center.cc index c994088c0a3..f2db0dba356 100644 --- a/chromium/ui/message_center/fake_message_center.cc +++ b/chromium/ui/message_center/fake_message_center.cc @@ -72,11 +72,7 @@ void FakeMessageCenter::RemoveNotification(const std::string& id, bool by_user) { } -void FakeMessageCenter::RemoveAllNotifications(bool by_user) { -} - -void FakeMessageCenter::RemoveAllVisibleNotifications(bool by_user) { -} +void FakeMessageCenter::RemoveAllNotifications(bool by_user, RemoveType type) {} void FakeMessageCenter::SetNotificationIcon(const std::string& notification_id, const gfx::Image& image) { diff --git a/chromium/ui/message_center/fake_message_center.h b/chromium/ui/message_center/fake_message_center.h index 003efbc7e64..cfbf8e97b37 100644 --- a/chromium/ui/message_center/fake_message_center.h +++ b/chromium/ui/message_center/fake_message_center.h @@ -40,8 +40,7 @@ class FakeMessageCenter : public MessageCenter { scoped_ptr<Notification> new_notification) override; void RemoveNotification(const std::string& id, bool by_user) override; - void RemoveAllNotifications(bool by_user) override; - void RemoveAllVisibleNotifications(bool by_user) override; + void RemoveAllNotifications(bool by_user, RemoveType type) override; void SetNotificationIcon(const std::string& notification_id, const gfx::Image& image) override; diff --git a/chromium/ui/message_center/fake_message_center_tray_delegate.h b/chromium/ui/message_center/fake_message_center_tray_delegate.h index cee0c79940a..d0e9579291f 100644 --- a/chromium/ui/message_center/fake_message_center_tray_delegate.h +++ b/chromium/ui/message_center/fake_message_center_tray_delegate.h @@ -34,7 +34,6 @@ class FakeMessageCenterTrayDelegate : public MessageCenterTrayDelegate { private: scoped_ptr<MessageCenterTray> tray_; base::Closure quit_closure_; - bool displayed_first_run_balloon_; DISALLOW_COPY_AND_ASSIGN(FakeMessageCenterTrayDelegate); }; diff --git a/chromium/ui/message_center/fake_notifier_settings_provider.cc b/chromium/ui/message_center/fake_notifier_settings_provider.cc index 79c153a8783..9cafc933e15 100644 --- a/chromium/ui/message_center/fake_notifier_settings_provider.cc +++ b/chromium/ui/message_center/fake_notifier_settings_provider.cc @@ -12,6 +12,9 @@ namespace message_center { FakeNotifierSettingsProvider::NotifierGroupItem::NotifierGroupItem() { } +FakeNotifierSettingsProvider::NotifierGroupItem::NotifierGroupItem( + const NotifierGroupItem& other) = default; + FakeNotifierSettingsProvider::NotifierGroupItem::~NotifierGroupItem() { } @@ -28,8 +31,7 @@ FakeNotifierSettingsProvider::FakeNotifierSettingsProvider( NotifierGroupItem item; item.group = new NotifierGroup(gfx::Image(), base::UTF8ToUTF16("Fake name"), - base::UTF8ToUTF16("fake@email.com"), - true); + base::UTF8ToUTF16("fake@email.com")); item.notifiers = notifiers; items_.push_back(item); } diff --git a/chromium/ui/message_center/fake_notifier_settings_provider.h b/chromium/ui/message_center/fake_notifier_settings_provider.h index d5a2c8a8382..e8e6c2ba8a4 100644 --- a/chromium/ui/message_center/fake_notifier_settings_provider.h +++ b/chromium/ui/message_center/fake_notifier_settings_provider.h @@ -56,6 +56,7 @@ class FakeNotifierSettingsProvider : public NotifierSettingsProvider { std::vector<Notifier*> notifiers; NotifierGroupItem(); + NotifierGroupItem(const NotifierGroupItem& other); ~NotifierGroupItem(); }; diff --git a/chromium/ui/message_center/message_center.gyp b/chromium/ui/message_center/message_center.gyp index 90f68388c34..0940f24419c 100644 --- a/chromium/ui/message_center/message_center.gyp +++ b/chromium/ui/message_center/message_center.gyp @@ -8,6 +8,7 @@ }, 'targets': [ { + # TODO(msw|mukai|dewittj): Move ash-specific files: crbug.com/585175 # GN version: //ui/message_center 'target_name': 'message_center', 'type': '<(component)', @@ -66,6 +67,8 @@ 'notifier_settings.h', 'popup_timer.cc', 'popup_timer.h', + 'popup_timers_controller.cc', + 'popup_timers_controller.h', 'views/bounded_label.cc', 'views/bounded_label.h', 'views/constants.h', @@ -129,14 +132,6 @@ ['exclude', 'views/'], ], }], - ['use_ash==0', { - 'sources!': [ - 'views/message_bubble_base.cc', - 'views/message_bubble_base.h', - 'views/message_center_bubble.cc', - 'views/message_center_bubble.h', - ], - }], # iOS disables notifications altogether, Android implements its own # notification UI manager instead of deferring to the message center. ['notifications==0 or OS=="android"', { @@ -239,12 +234,6 @@ ['include', '^test/run_all_unittests\\.cc$'], ], }], - # See http://crbug.com/162998#c4 for why this is needed. - ['OS=="linux" and use_allocator!="none"', { - 'dependencies': [ - '../../base/allocator/allocator.gyp:allocator', - ], - }], ], }, # target_name: message_center_unittests ], diff --git a/chromium/ui/message_center/message_center.h b/chromium/ui/message_center/message_center.h index 00d9545d639..d6ce0c38acb 100644 --- a/chromium/ui/message_center/message_center.h +++ b/chromium/ui/message_center/message_center.h @@ -50,6 +50,13 @@ class NotifierSettingsProvider; class MESSAGE_CENTER_EXPORT MessageCenter { public: + enum class RemoveType { + // Remove all notifications. + ALL, + // Remove non-pinned notification (don't remove invisible ones). + NON_PINNED, + }; + // Creates the global message center object. static void Initialize(); @@ -102,8 +109,7 @@ class MESSAGE_CENTER_EXPORT MessageCenter { // Removes an existing notification. virtual void RemoveNotification(const std::string& id, bool by_user) = 0; - virtual void RemoveAllNotifications(bool by_user) = 0; - virtual void RemoveAllVisibleNotifications(bool by_user) = 0; + virtual void RemoveAllNotifications(bool by_user, RemoveType type) = 0; // Sets the icon image. Icon appears at the top-left of the notification. virtual void SetNotificationIcon(const std::string& notification_id, diff --git a/chromium/ui/message_center/message_center_impl.cc b/chromium/ui/message_center/message_center_impl.cc index c1a9e122cad..07cbe104ca0 100644 --- a/chromium/ui/message_center/message_center_impl.cc +++ b/chromium/ui/message_center/message_center_impl.cc @@ -21,7 +21,7 @@ #include "ui/message_center/notification_blocker.h" #include "ui/message_center/notification_list.h" #include "ui/message_center/notification_types.h" -#include "ui/message_center/popup_timer.h" +#include "ui/message_center/popup_timers_controller.h" namespace message_center { namespace internal { @@ -643,23 +643,20 @@ void MessageCenterImpl::RemoveNotificationsForNotifierId( } } -void MessageCenterImpl::RemoveAllNotifications(bool by_user) { - // Using not |blockers_| but an empty list since it wants to remove literally - // all notifications. - RemoveNotifications(by_user, NotificationBlockers()); -} +void MessageCenterImpl::RemoveAllNotifications(bool by_user, RemoveType type) { + bool remove_pinned = (type == RemoveType::NON_PINNED); -void MessageCenterImpl::RemoveAllVisibleNotifications(bool by_user) { - RemoveNotifications(by_user, blockers_); -} + const NotificationBlockers& blockers = + (type == RemoveType::ALL ? NotificationBlockers() /* empty blockers */ + : blockers_ /* use default blockers */); -void MessageCenterImpl::RemoveNotifications( - bool by_user, - const NotificationBlockers& blockers) { const NotificationList::Notifications notifications = notification_list_->GetVisibleNotifications(blockers); std::set<std::string> ids; for (const auto& notification : notifications) { + if (!remove_pinned && notification->pinned()) + continue; + ids.insert(notification->id()); scoped_refptr<NotificationDelegate> delegate = notification->delegate(); if (delegate.get()) diff --git a/chromium/ui/message_center/message_center_impl.h b/chromium/ui/message_center/message_center_impl.h index 27997922a1b..03dc9bc78f4 100644 --- a/chromium/ui/message_center/message_center_impl.h +++ b/chromium/ui/message_center/message_center_impl.h @@ -20,7 +20,7 @@ #include "ui/message_center/message_center_types.h" #include "ui/message_center/notification_blocker.h" #include "ui/message_center/notifier_settings.h" -#include "ui/message_center/popup_timer.h" +#include "ui/message_center/popup_timers_controller.h" namespace message_center { class NotificationDelegate; @@ -58,8 +58,7 @@ class MessageCenterImpl : public MessageCenter, void UpdateNotification(const std::string& old_id, scoped_ptr<Notification> new_notification) override; void RemoveNotification(const std::string& id, bool by_user) override; - void RemoveAllNotifications(bool by_user) override; - void RemoveAllVisibleNotifications(bool by_user) override; + void RemoveAllNotifications(bool by_user, RemoveType type) override; void SetNotificationIcon(const std::string& notification_id, const gfx::Image& image) override; void SetNotificationImage(const std::string& notification_id, @@ -116,7 +115,6 @@ class MessageCenterImpl : public MessageCenter, size_t unread_count; }; - void RemoveNotifications(bool by_user, const NotificationBlockers& blockers); void RemoveNotificationsForNotifierId(const NotifierId& notifier_id); scoped_ptr<NotificationList> notification_list_; diff --git a/chromium/ui/message_center/message_center_impl_unittest.cc b/chromium/ui/message_center/message_center_impl_unittest.cc index 2b405fe6de4..bc3828bc994 100644 --- a/chromium/ui/message_center/message_center_impl_unittest.cc +++ b/chromium/ui/message_center/message_center_impl_unittest.cc @@ -559,9 +559,10 @@ TEST_F(MessageCenterImplTest, TotalNotificationBlocker) { EXPECT_TRUE(NotificationsContain(notifications, "id3")); EXPECT_TRUE(NotificationsContain(notifications, "id4")); - // RemoveAllVisibleNotifications should remove just visible notifications. + // Remove just visible notifications. blocker.SetNotificationsEnabled(false); - message_center()->RemoveAllVisibleNotifications(false /* by_user */); + message_center()->RemoveAllNotifications( + false /* by_user */, MessageCenter::RemoveType::NON_PINNED); EXPECT_EQ(0u, message_center()->NotificationCount()); blocker.SetNotificationsEnabled(true); EXPECT_EQ(2u, message_center()->NotificationCount()); @@ -571,9 +572,10 @@ TEST_F(MessageCenterImplTest, TotalNotificationBlocker) { EXPECT_TRUE(NotificationsContain(notifications, "id3")); EXPECT_FALSE(NotificationsContain(notifications, "id4")); - // And RemoveAllNotifications should remove all. + // And remove all including invisible notifications. blocker.SetNotificationsEnabled(false); - message_center()->RemoveAllNotifications(false /* by_user */); + message_center()->RemoveAllNotifications(false /* by_user */, + MessageCenter::RemoveType::ALL); EXPECT_EQ(0u, message_center()->NotificationCount()); } diff --git a/chromium/ui/message_center/notification.cc b/chromium/ui/message_center/notification.cc index 9de5ccb3675..b0a379ddb9d 100644 --- a/chromium/ui/message_center/notification.cc +++ b/chromium/ui/message_center/notification.cc @@ -34,6 +34,10 @@ RichNotificationData::RichNotificationData() progress(0), should_make_spoken_feedback_for_popup_updates(true), clickable(true), +#if defined(OS_CHROMEOS) + pinned(false), +#endif // defined(OS_CHROMEOS) + renotify(false), silent(false) {} RichNotificationData::RichNotificationData(const RichNotificationData& other) @@ -49,7 +53,11 @@ RichNotificationData::RichNotificationData(const RichNotificationData& other) should_make_spoken_feedback_for_popup_updates( other.should_make_spoken_feedback_for_popup_updates), clickable(other.clickable), +#if defined(OS_CHROMEOS) + pinned(other.pinned), +#endif // defined(OS_CHROMEOS) vibration_pattern(other.vibration_pattern), + renotify(other.renotify), silent(other.silent) {} RichNotificationData::~RichNotificationData() {} @@ -69,7 +77,7 @@ Notification::Notification(NotificationType type, title_(title), message_(message), icon_(icon), - adjust_icon_(true), + draw_icon_background_(true), display_source_(display_source), origin_url_(origin_url), notifier_id_(notifier_id), @@ -85,7 +93,7 @@ Notification::Notification(const std::string& id, const Notification& other) title_(other.title_), message_(other.message_), icon_(other.icon_), - adjust_icon_(other.adjust_icon_), + draw_icon_background_(other.draw_icon_background_), display_source_(other.display_source_), origin_url_(other.origin_url_), notifier_id_(other.notifier_id_), @@ -101,7 +109,7 @@ Notification::Notification(const Notification& other) title_(other.title_), message_(other.message_), icon_(other.icon_), - adjust_icon_(other.adjust_icon_), + draw_icon_background_(other.draw_icon_background_), display_source_(other.display_source_), origin_url_(other.origin_url_), notifier_id_(other.notifier_id_), @@ -117,7 +125,7 @@ Notification& Notification::operator=(const Notification& other) { title_ = other.title_; message_ = other.message_; icon_ = other.icon_; - adjust_icon_ = other.adjust_icon_; + draw_icon_background_ = other.draw_icon_background_; display_source_ = other.display_source_; origin_url_ = other.origin_url_; notifier_id_ = other.notifier_id_; diff --git a/chromium/ui/message_center/notification.h b/chromium/ui/message_center/notification.h index 491943d6e07..f06e92dd29a 100644 --- a/chromium/ui/message_center/notification.h +++ b/chromium/ui/message_center/notification.h @@ -53,7 +53,13 @@ class MESSAGE_CENTER_EXPORT RichNotificationData { std::vector<ButtonInfo> buttons; bool should_make_spoken_feedback_for_popup_updates; bool clickable; +#if defined(OS_CHROMEOS) + // Flag if the notification is pinned. If true, the notification is pinned + // and user can't remove it. + bool pinned; +#endif // defined(OS_CHROMEOS) std::vector<int> vibration_pattern; + bool renotify; bool silent; }; @@ -123,6 +129,13 @@ class MESSAGE_CENTER_EXPORT Notification { optional_fields_.vibration_pattern = vibration_pattern; } + // This property currently only works in platforms that support native + // notifications. + // It determines whether the sound and vibration effects should signal + // if the notification is replacing another notification. + bool renotify() const { return optional_fields_.renotify; } + void set_renotify(bool renotify) { optional_fields_.renotify = renotify; } + // This property currently has no effect on non-Android platforms. bool silent() const { return optional_fields_.silent; } void set_silent(bool silent) { optional_fields_.silent = silent; } @@ -158,12 +171,10 @@ class MESSAGE_CENTER_EXPORT Notification { const gfx::Image& icon() const { return icon_; } void set_icon(const gfx::Image& icon) { icon_ = icon; } - // Gets and sets whether to adjust the icon before displaying. The adjustment - // is designed to accomodate legacy HTML icons but isn't necessary for - // Chrome's hardcoded notifications. NB: this is currently ignored outside of - // Views. - bool adjust_icon() const { return adjust_icon_; } - void set_adjust_icon(bool adjust) { adjust_icon_ = adjust; } + // Gets and sets whether to draw a solid background colour behind the + // notification's icon. Only applies to the Views implementation. + bool draw_icon_background() const { return draw_icon_background_; } + void set_draw_icon_background(bool draw) { draw_icon_background_ = draw; } const gfx::Image& image() const { return optional_fields_.image; } void set_image(const gfx::Image& image) { optional_fields_.image = image; } @@ -206,6 +217,17 @@ class MESSAGE_CENTER_EXPORT Notification { optional_fields_.clickable = clickable; } + bool pinned() const { +#if defined(OS_CHROMEOS) + return optional_fields_.pinned; +#else + return false; +#endif // defined(OS_CHROMEOS) + } +#if defined(OS_CHROMEOS) + void set_pinned(bool pinned) { optional_fields_.pinned = pinned; } +#endif // defined(OS_CHROMEOS) + NotificationDelegate* delegate() const { return delegate_.get(); } const RichNotificationData& rich_notification_data() const { @@ -246,9 +268,9 @@ class MESSAGE_CENTER_EXPORT Notification { // Image data for the associated icon, used by Ash when available. gfx::Image icon_; - // True by default; controls whether to apply adjustments such as BG color and - // size scaling to |icon_|. - bool adjust_icon_; + // True by default; controls whether to draw a solid background colour behind + // the |icon_|. Only applies to the Views implementation. + bool draw_icon_background_; // The display string for the source of the notification. Could be // the same as origin_url_, or the name of an extension. diff --git a/chromium/ui/message_center/notifier_settings.cc b/chromium/ui/message_center/notifier_settings.cc index 26546118b9e..f5e9c012b5d 100644 --- a/chromium/ui/message_center/notifier_settings.cc +++ b/chromium/ui/message_center/notifier_settings.cc @@ -24,6 +24,8 @@ NotifierId::NotifierId() : type(SYSTEM_COMPONENT) { } +NotifierId::NotifierId(const NotifierId& other) = default; + bool NotifierId::operator==(const NotifierId& other) const { if (type != other.type) return false; @@ -63,9 +65,8 @@ Notifier::~Notifier() { NotifierGroup::NotifierGroup(const gfx::Image& icon, const base::string16& name, - const base::string16& login_info, - size_t index) - : icon(icon), name(name), login_info(login_info), index(index) {} + const base::string16& login_info) + : icon(icon), name(name), login_info(login_info) {} NotifierGroup::~NotifierGroup() {} diff --git a/chromium/ui/message_center/notifier_settings.h b/chromium/ui/message_center/notifier_settings.h index 95646afcb39..f01a069f852 100644 --- a/chromium/ui/message_center/notifier_settings.h +++ b/chromium/ui/message_center/notifier_settings.h @@ -8,6 +8,7 @@ #include <stddef.h> #include <string> +#include <vector> #include "base/gtest_prod_util.h" #include "base/macros.h" @@ -53,6 +54,8 @@ struct MESSAGE_CENTER_EXPORT NotifierId { // Constructor for WEB_PAGE type. explicit NotifierId(const GURL& url); + NotifierId(const NotifierId& other); + bool operator==(const NotifierId& other) const; // Allows NotifierId to be used as a key in std::map. bool operator<(const NotifierId& other) const; @@ -114,8 +117,7 @@ struct MESSAGE_CENTER_EXPORT Notifier { struct MESSAGE_CENTER_EXPORT NotifierGroup { NotifierGroup(const gfx::Image& icon, const base::string16& name, - const base::string16& login_info, - size_t index); + const base::string16& login_info); ~NotifierGroup(); // Icon of a notifier group. @@ -127,10 +129,6 @@ struct MESSAGE_CENTER_EXPORT NotifierGroup { // More display information about the notifier group. base::string16 login_info; - // Unique identifier for the notifier group so that they can be selected in - // the UI. - const size_t index; - private: DISALLOW_COPY_AND_ASSIGN(NotifierGroup); }; @@ -155,7 +153,7 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsObserver { // for the clients of this module. class MESSAGE_CENTER_EXPORT NotifierSettingsProvider { public: - virtual ~NotifierSettingsProvider() {}; + virtual ~NotifierSettingsProvider() {} // Sets the delegate. virtual void AddObserver(NotifierSettingsObserver* observer) = 0; diff --git a/chromium/ui/message_center/popup_timer.cc b/chromium/ui/message_center/popup_timer.cc index f19da62e8df..c79377df32a 100644 --- a/chromium/ui/message_center/popup_timer.cc +++ b/chromium/ui/message_center/popup_timer.cc @@ -4,37 +4,14 @@ #include "ui/message_center/popup_timer.h" -#include <algorithm> - -#include "base/stl_util.h" -#include "ui/message_center/message_center_style.h" -#include "ui/message_center/message_center_types.h" -#include "ui/message_center/notification.h" -#include "ui/message_center/notification_list.h" - namespace message_center { -namespace { - -base::TimeDelta GetTimeoutForNotification(Notification* notification) { - if (notification->priority() > DEFAULT_PRIORITY) - return base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds); - if (notification->notifier_id().type == NotifierId::WEB_PAGE) - return base::TimeDelta::FromSeconds(kAutocloseWebPageDelaySeconds); - return base::TimeDelta::FromSeconds(kAutocloseDefaultDelaySeconds); -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// PopupTimer - PopupTimer::PopupTimer(const std::string& id, base::TimeDelta timeout, - base::WeakPtr<PopupTimersController> controller) + base::WeakPtr<Delegate> delegate) : id_(id), timeout_(timeout), - timer_controller_(controller), + timer_delegate_(delegate), timer_(new base::OneShotTimer) {} PopupTimer::~PopupTimer() { @@ -51,9 +28,11 @@ void PopupTimer::Start() { base::TimeDelta timeout_to_close = timeout_ <= passed_ ? base::TimeDelta() : timeout_ - passed_; start_time_ = base::Time::Now(); - timer_->Start(FROM_HERE, timeout_to_close, - base::Bind(&PopupTimersController::TimerFinished, - timer_controller_, id_)); + + DCHECK(timer_delegate_); + timer_->Start( + FROM_HERE, timeout_to_close, + base::Bind(&Delegate::TimerFinished, timer_delegate_, id_)); } void PopupTimer::Pause() { @@ -70,107 +49,4 @@ void PopupTimer::Reset() { passed_ = base::TimeDelta(); } -//////////////////////////////////////////////////////////////////////////////// -// PopupTimersController - -PopupTimersController::PopupTimersController(MessageCenter* message_center) - : message_center_(message_center) { - message_center_->AddObserver(this); -} - -PopupTimersController::~PopupTimersController() { - message_center_->RemoveObserver(this); -} - -void PopupTimersController::StartTimer(const std::string& id, - const base::TimeDelta& timeout) { - PopupTimerCollection::const_iterator iter = popup_timers_.find(id); - if (iter != popup_timers_.end()) { - DCHECK(iter->second); - iter->second->Start(); - return; - } - - scoped_ptr<PopupTimer> timer(new PopupTimer(id, timeout, AsWeakPtr())); - - timer->Start(); - popup_timers_.insert(std::make_pair(id, std::move(timer))); -} - -void PopupTimersController::StartAll() { - for (const auto& iter : popup_timers_) - iter.second->Start(); -} - -void PopupTimersController::ResetTimer(const std::string& id, - const base::TimeDelta& timeout) { - CancelTimer(id); - StartTimer(id, timeout); -} - -void PopupTimersController::PauseTimer(const std::string& id) { - PopupTimerCollection::const_iterator iter = popup_timers_.find(id); - if (iter == popup_timers_.end()) - return; - iter->second->Pause(); -} - -void PopupTimersController::PauseAll() { - for (const auto& iter : popup_timers_) - iter.second->Pause(); -} - -void PopupTimersController::CancelTimer(const std::string& id) { - popup_timers_.erase(id); -} - -void PopupTimersController::CancelAll() { - popup_timers_.clear(); -} - -void PopupTimersController::TimerFinished(const std::string& id) { - if (!ContainsKey(popup_timers_, id)) - return; - - CancelTimer(id); - message_center_->MarkSinglePopupAsShown(id, false); -} - -void PopupTimersController::OnNotificationDisplayed( - const std::string& id, - const DisplaySource source) { - OnNotificationUpdated(id); -} - -void PopupTimersController::OnNotificationUpdated(const std::string& id) { - NotificationList::PopupNotifications popup_notifications = - message_center_->GetPopupNotifications(); - - if (!popup_notifications.size()) { - CancelAll(); - return; - } - - NotificationList::PopupNotifications::const_iterator iter = - popup_notifications.begin(); - for (; iter != popup_notifications.end(); ++iter) { - if ((*iter)->id() == id) - break; - } - - if (iter == popup_notifications.end() || (*iter)->never_timeout()) { - CancelTimer(id); - return; - } - - // Start the timer if not yet. - if (popup_timers_.find(id) == popup_timers_.end()) - StartTimer(id, GetTimeoutForNotification(*iter)); -} - -void PopupTimersController::OnNotificationRemoved(const std::string& id, - bool by_user) { - CancelTimer(id); -} - } // namespace message_center diff --git a/chromium/ui/message_center/popup_timer.h b/chromium/ui/message_center/popup_timer.h index 7ffca1b14f0..8fa39eb3ff3 100644 --- a/chromium/ui/message_center/popup_timer.h +++ b/chromium/ui/message_center/popup_timer.h @@ -5,38 +5,36 @@ #ifndef UI_MESSAGE_CENTER_POPUP_TIMER_H_ #define UI_MESSAGE_CENTER_POPUP_TIMER_H_ -#include <map> #include <string> -#include <vector> #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "base/timer/timer.h" -#include "ui/message_center/message_center.h" -#include "ui/message_center/message_center_observer.h" -#include "ui/message_center/message_center_types.h" -#include "ui/message_center/notification_blocker.h" -#include "ui/message_center/notifier_settings.h" namespace message_center { -class PopupTimersController; // A class that manages timeout behavior for notification popups. One instance // is created per notification popup. class PopupTimer { public: + // Host the callback for each timer when its time is up. + class Delegate { + public: + virtual void TimerFinished(const std::string& id) = 0; + }; + // Accepts a notification ID, time until callback, and a reference to the - // controller which will be called back. The reference is a weak pointer so + // delegate which will be called back. The reference is a weak pointer so // that timers never cause a callback on a destructed object. PopupTimer(const std::string& id, base::TimeDelta timeout, - base::WeakPtr<PopupTimersController> controller); + base::WeakPtr<Delegate> delegate); ~PopupTimer(); // Starts running the timer. Barring a Pause or Reset call, the timer will - // call back to |controller| after |timeout| seconds. + // call back to |delegate| after |timeout| seconds. void Start(); // Stops the timer, and retains the amount of time that has passed so that on @@ -63,7 +61,7 @@ class PopupTimer { base::Time start_time_; // Callback recipient. - base::WeakPtr<PopupTimersController> timer_controller_; + base::WeakPtr<Delegate> timer_delegate_; // The actual timer. scoped_ptr<base::OneShotTimer> timer_; @@ -71,60 +69,6 @@ class PopupTimer { DISALLOW_COPY_AND_ASSIGN(PopupTimer); }; -// A class that manages all the timers running for individual notification popup -// windows. It supports weak pointers in order to allow safe callbacks when -// timers expire. -class MESSAGE_CENTER_EXPORT PopupTimersController - : public base::SupportsWeakPtr<PopupTimersController>, - public MessageCenterObserver { - public: - explicit PopupTimersController(MessageCenter* message_center); - ~PopupTimersController() override; - - // MessageCenterObserver implementation. - void OnNotificationDisplayed(const std::string& id, - const DisplaySource source) override; - void OnNotificationUpdated(const std::string& id) override; - void OnNotificationRemoved(const std::string& id, bool by_user) override; - - // Callback for each timer when its time is up. - virtual void TimerFinished(const std::string& id); - - // Pauses all running timers. - void PauseAll(); - - // Continues all managed timers. - void StartAll(); - - // Removes all managed timers. - void CancelAll(); - - // Starts a timer (by creating a PopupTimer) for |id|. - void StartTimer(const std::string& id, - const base::TimeDelta& timeout_in_seconds); - - // Stops a single timer, reverts it to a new timeout, and restarts it. - void ResetTimer(const std::string& id, - const base::TimeDelta& timeout_in_seconds); - - // Pauses a single timer, such that it will continue where it left off after a - // call to StartAll or StartTimer. - void PauseTimer(const std::string& id); - - // Removes and cancels a single popup timer, if it exists. - void CancelTimer(const std::string& id); - - private: - // Weak, global. - MessageCenter* message_center_; - - // The PopupTimerCollection contains all the managed timers by their ID. - using PopupTimerCollection = std::map<std::string, scoped_ptr<PopupTimer>>; - PopupTimerCollection popup_timers_; - - DISALLOW_COPY_AND_ASSIGN(PopupTimersController); -}; - } // namespace message_center #endif // UI_MESSAGE_CENTER_POPUP_TIMER_H_ diff --git a/chromium/ui/message_center/popup_timers_controller.cc b/chromium/ui/message_center/popup_timers_controller.cc new file mode 100644 index 00000000000..14c9065940a --- /dev/null +++ b/chromium/ui/message_center/popup_timers_controller.cc @@ -0,0 +1,126 @@ +// 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/message_center/popup_timers_controller.h" + +#include <algorithm> + +#include "base/stl_util.h" +#include "ui/message_center/message_center_style.h" + +namespace message_center { + +namespace { + +base::TimeDelta GetTimeoutForNotification(Notification* notification) { + if (notification->priority() > DEFAULT_PRIORITY) + return base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds); + if (notification->notifier_id().type == NotifierId::WEB_PAGE) + return base::TimeDelta::FromSeconds(kAutocloseWebPageDelaySeconds); + return base::TimeDelta::FromSeconds(kAutocloseDefaultDelaySeconds); +} + +} // namespace + +PopupTimersController::PopupTimersController(MessageCenter* message_center) + : message_center_(message_center) { + message_center_->AddObserver(this); +} + +PopupTimersController::~PopupTimersController() { + message_center_->RemoveObserver(this); +} + +void PopupTimersController::StartTimer(const std::string& id, + const base::TimeDelta& timeout) { + PopupTimerCollection::const_iterator iter = popup_timers_.find(id); + if (iter != popup_timers_.end()) { + DCHECK(iter->second); + iter->second->Start(); + return; + } + + scoped_ptr<PopupTimer> timer(new PopupTimer(id, timeout, AsWeakPtr())); + + timer->Start(); + popup_timers_.insert(std::make_pair(id, std::move(timer))); +} + +void PopupTimersController::StartAll() { + for (const auto& iter : popup_timers_) + iter.second->Start(); +} + +void PopupTimersController::ResetTimer(const std::string& id, + const base::TimeDelta& timeout) { + CancelTimer(id); + StartTimer(id, timeout); +} + +void PopupTimersController::PauseTimer(const std::string& id) { + PopupTimerCollection::const_iterator iter = popup_timers_.find(id); + if (iter == popup_timers_.end()) + return; + iter->second->Pause(); +} + +void PopupTimersController::PauseAll() { + for (const auto& iter : popup_timers_) + iter.second->Pause(); +} + +void PopupTimersController::CancelTimer(const std::string& id) { + popup_timers_.erase(id); +} + +void PopupTimersController::CancelAll() { + popup_timers_.clear(); +} + +void PopupTimersController::TimerFinished(const std::string& id) { + if (!ContainsKey(popup_timers_, id)) + return; + + CancelTimer(id); + message_center_->MarkSinglePopupAsShown(id, false); +} + +void PopupTimersController::OnNotificationDisplayed( + const std::string& id, + const DisplaySource source) { + OnNotificationUpdated(id); +} + +void PopupTimersController::OnNotificationUpdated(const std::string& id) { + NotificationList::PopupNotifications popup_notifications = + message_center_->GetPopupNotifications(); + + if (!popup_notifications.size()) { + CancelAll(); + return; + } + + NotificationList::PopupNotifications::const_iterator iter = + popup_notifications.begin(); + for (; iter != popup_notifications.end(); ++iter) { + if ((*iter)->id() == id) + break; + } + + if (iter == popup_notifications.end() || (*iter)->never_timeout()) { + CancelTimer(id); + return; + } + + // Start the timer if not yet. + if (popup_timers_.find(id) == popup_timers_.end()) + StartTimer(id, GetTimeoutForNotification(*iter)); +} + +void PopupTimersController::OnNotificationRemoved(const std::string& id, + bool by_user) { + CancelTimer(id); +} + +} // namespace message_center diff --git a/chromium/ui/message_center/popup_timers_controller.h b/chromium/ui/message_center/popup_timers_controller.h new file mode 100644 index 00000000000..d2e2db729cb --- /dev/null +++ b/chromium/ui/message_center/popup_timers_controller.h @@ -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. + +#ifndef UI_MESSAGE_CENTER_POPUP_TIMERS_CONTROLLER_H_ +#define UI_MESSAGE_CENTER_POPUP_TIMERS_CONTROLLER_H_ + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/message_center_observer.h" +#include "ui/message_center/popup_timer.h" + +namespace message_center { + +// A class that manages all the timers running for individual notification popup +// windows. It supports weak pointers in order to allow safe callbacks when +// timers expire. +// We can use SupportsWeakPtr<> because PopupTimer does not +// access this class in its destructor so it is safe to invalidate weak pointers +// after we destroy |popup_timers_| +class MESSAGE_CENTER_EXPORT PopupTimersController + : public base::SupportsWeakPtr<PopupTimersController>, + public MessageCenterObserver, + public PopupTimer::Delegate { + public: + explicit PopupTimersController(MessageCenter* message_center); + ~PopupTimersController() override; + + // MessageCenterObserver implementation. + void OnNotificationDisplayed(const std::string& id, + const DisplaySource source) override; + void OnNotificationUpdated(const std::string& id) override; + void OnNotificationRemoved(const std::string& id, bool by_user) override; + + // PopupTimer::Delegate implementation + void TimerFinished(const std::string& id) override; + + // Pauses all running timers. + void PauseAll(); + + // Continues all managed timers. + void StartAll(); + + // Removes all managed timers. + void CancelAll(); + + // Starts a timer (by creating a PopupTimer) for |id|. + void StartTimer(const std::string& id, + const base::TimeDelta& timeout_in_seconds); + + // Stops a single timer, reverts it to a new timeout, and restarts it. + void ResetTimer(const std::string& id, + const base::TimeDelta& timeout_in_seconds); + + // Pauses a single timer, such that it will continue where it left off after a + // call to StartAll or StartTimer. + void PauseTimer(const std::string& id); + + // Removes and cancels a single popup timer, if it exists. + void CancelTimer(const std::string& id); + + private: + // Weak, global. + MessageCenter* message_center_; + + // The PopupTimerCollection contains all the managed timers by their ID. + using PopupTimerCollection = std::map<std::string, scoped_ptr<PopupTimer>>; + PopupTimerCollection popup_timers_; + + DISALLOW_COPY_AND_ASSIGN(PopupTimersController); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_POPUP_TIMERS_CONTROLLER_H_ diff --git a/chromium/ui/message_center/views/constants.h b/chromium/ui/message_center/views/constants.h index 2600d9182b0..9942ed8a1e1 100644 --- a/chromium/ui/message_center/views/constants.h +++ b/chromium/ui/message_center/views/constants.h @@ -20,8 +20,6 @@ const SkColor kRegularTextBackgroundColor = SK_ColorWHITE; const SkColor kDimTextBackgroundColor = SK_ColorWHITE; const SkColor kContextTextBackgroundColor = SK_ColorWHITE; -const int kIconSize = message_center::kNotificationIconSize; -const int kLegacyIconSize = 40; const int kTextBottomPadding = 12; const int kItemTitleToMessagePadding = 3; const int kButtonVecticalPadding = 0; 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 6a11fd56222..fbd5859d9ba 100644 --- a/chromium/ui/message_center/views/message_center_button_bar.cc +++ b/chromium/ui/message_center/views/message_center_button_bar.cc @@ -267,6 +267,10 @@ void MessageCenterButtonBar::SetCloseAllButtonEnabled(bool enabled) { close_all_button_->SetEnabled(enabled); } +views::Button* MessageCenterButtonBar::GetCloseAllButtonForTest() const { + return close_all_button_; +} + void MessageCenterButtonBar::SetBackArrowVisible(bool visible) { if (title_arrow_) title_arrow_->SetVisible(visible); @@ -281,7 +285,7 @@ void MessageCenterButtonBar::ChildVisibilityChanged(views::View* child) { void MessageCenterButtonBar::ButtonPressed(views::Button* sender, const ui::Event& event) { if (sender == close_all_button_) { - message_center_view()->ClearAllNotifications(); + message_center_view()->ClearAllClosableNotifications(); } else if (sender == settings_button_ || sender == title_arrow_) { MessageCenterView* center_view = message_center_view(); center_view->SetSettingsVisible(!center_view->settings_visible()); diff --git a/chromium/ui/message_center/views/message_center_button_bar.h b/chromium/ui/message_center/views/message_center_button_bar.h index 74c7b846405..e7932abd1b5 100644 --- a/chromium/ui/message_center/views/message_center_button_bar.h +++ b/chromium/ui/message_center/views/message_center_button_bar.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "build/build_config.h" +#include "ui/message_center/message_center_export.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/view.h" @@ -43,6 +44,8 @@ class MessageCenterButtonBar : public views::View, // Sometimes we shouldn't see the close-all button. void SetCloseAllButtonEnabled(bool enabled); + MESSAGE_CENTER_EXPORT views::Button* GetCloseAllButtonForTest() const; + // Sometimes we shouldn't see the back arrow (not in settings). void SetBackArrowVisible(bool visible); diff --git a/chromium/ui/message_center/views/message_center_view.cc b/chromium/ui/message_center/views/message_center_view.cc index dc6d527d59e..c8664ed7a65 100644 --- a/chromium/ui/message_center/views/message_center_view.cc +++ b/chromium/ui/message_center/views/message_center_view.cc @@ -148,7 +148,7 @@ MessageCenterView::MessageCenterView(MessageCenter* message_center, views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); scroller_->SetPaintToLayer(true); - scroller_->SetFillsBoundsOpaquely(false); + scroller_->layer()->SetFillsBoundsOpaquely(false); scroller_->layer()->SetMasksToBounds(true); empty_list_view_.reset(new NoNotificationMessageView); @@ -255,20 +255,25 @@ void MessageCenterView::SetSettingsVisible(bool visible) { button_bar_->SetBackArrowVisible(visible); } -void MessageCenterView::ClearAllNotifications() { +void MessageCenterView::ClearAllClosableNotifications() { if (is_closing_) return; SetViewHierarchyEnabled(scroller_, false); button_bar_->SetAllButtonsEnabled(false); - message_list_view_->ClearAllNotifications(scroller_->GetVisibleRect()); + message_list_view_->ClearAllClosableNotifications( + scroller_->GetVisibleRect()); } void MessageCenterView::OnAllNotificationsCleared() { SetViewHierarchyEnabled(scroller_, true); button_bar_->SetAllButtonsEnabled(true); button_bar_->SetCloseAllButtonEnabled(false); - message_center_->RemoveAllVisibleNotifications(true); // Action by user. + + // Action by user. + message_center_->RemoveAllNotifications( + true /* by_user */, + message_center::MessageCenter::RemoveType::NON_PINNED); } size_t MessageCenterView::NumMessageViewsForTest() const { @@ -588,7 +593,14 @@ void MessageCenterView::NotificationsChanged() { scroller_->contents()->AddChildView( no_message_views ? empty_list_view_.get() : message_list_view_.get()); - button_bar_->SetCloseAllButtonEnabled(!no_message_views); + bool no_closable_views = true; + for (const auto& view : notification_views_) { + if (!view.second->IsPinned()) { + no_closable_views = false; + break; + } + } + button_bar_->SetCloseAllButtonEnabled(!no_closable_views); scroller_->SetFocusable(!no_message_views); if (focus_manager && focused_view) diff --git a/chromium/ui/message_center/views/message_center_view.h b/chromium/ui/message_center/views/message_center_view.h index af91c4ad402..69d890da77c 100644 --- a/chromium/ui/message_center/views/message_center_view.h +++ b/chromium/ui/message_center/views/message_center_view.h @@ -50,7 +50,7 @@ class MESSAGE_CENTER_EXPORT MessageCenterView : public views::View, void SetNotifications(const NotificationList::Notifications& notifications); - void ClearAllNotifications(); + void ClearAllClosableNotifications(); void OnAllNotificationsCleared(); size_t NumMessageViewsForTest() const; 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 d412cb04416..1d592a3312c 100644 --- a/chromium/ui/message_center/views/message_center_view_unittest.cc +++ b/chromium/ui/message_center/views/message_center_view_unittest.cc @@ -17,9 +17,11 @@ #include "ui/message_center/notification.h" #include "ui/message_center/notification_list.h" #include "ui/message_center/notification_types.h" +#include "ui/message_center/views/message_center_button_bar.h" #include "ui/message_center/views/message_center_controller.h" #include "ui/message_center/views/message_list_view.h" #include "ui/message_center/views/notification_view.h" +#include "ui/views/controls/slide_out_view.h" namespace message_center { @@ -34,6 +36,12 @@ enum CallType { LAYOUT }; +class DummyEvent : public ui::Event { + public: + DummyEvent() : Event(ui::ET_UNKNOWN, base::TimeDelta(), 0) {} + ~DummyEvent() override {} +}; + /* Instrumented/Mock NotificationView subclass ********************************/ class MockNotificationView : public NotificationView { @@ -94,6 +102,11 @@ class FakeMessageCenterImpl : public FakeMessageCenter { void SetVisibleNotifications(NotificationList::Notifications notifications) { visible_notifications_ = notifications; } + void RemoveAllNotifications(bool by_user, RemoveType type) override { + if (type == RemoveType::NON_PINNED) + remove_all_closable_notification_called_ = true; + } + bool remove_all_closable_notification_called_ = false; NotificationList::Notifications visible_notifications_; }; @@ -111,6 +124,7 @@ class MessageCenterViewTest : public testing::Test, MessageCenterView* GetMessageCenterView(); MessageListView* GetMessageListView(); + FakeMessageCenterImpl* GetMessageCenter() const; NotificationView* GetNotificationView(const std::string& id); views::BoundsAnimator* GetAnimator(); int GetNotificationCount(); @@ -139,6 +153,8 @@ class MessageCenterViewTest : public testing::Test, void LogBounds(int depth, views::View* view); + MessageCenterButtonBar* GetButtonBar() const; + private: views::View* MakeParent(views::View* child1, views::View* child2); @@ -146,7 +162,7 @@ class MessageCenterViewTest : public testing::Test, NotificationList::Notifications notifications_; scoped_ptr<MessageCenterView> message_center_view_; - FakeMessageCenterImpl message_center_; + scoped_ptr<FakeMessageCenterImpl> message_center_; std::map<CallType,int> callCounts_; DISALLOW_COPY_AND_ASSIGN(MessageCenterViewTest); @@ -159,6 +175,8 @@ MessageCenterViewTest::~MessageCenterViewTest() { } void MessageCenterViewTest::SetUp() { + message_center_.reset(new FakeMessageCenterImpl()); + // Create a dummy notification. Notification* notification1 = new Notification( NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId1), @@ -177,12 +195,12 @@ void MessageCenterViewTest::SetUp() { // ...and a list for it. notifications_.insert(notification1); notifications_.insert(notification2); - message_center_.SetVisibleNotifications(notifications_); + message_center_->SetVisibleNotifications(notifications_); // Then create a new MessageCenterView with that single notification. base::string16 title; message_center_view_.reset(new MessageCenterView( - &message_center_, NULL, 100, false, /*top_down =*/false, title)); + message_center_.get(), NULL, 100, false, /*top_down =*/false, title)); GetMessageListView()->quit_message_loop_after_animation_for_test_ = true; GetMessageCenterView()->SetBounds(0, 0, 380, 600); message_center_view_->SetNotifications(notifications_); @@ -205,6 +223,10 @@ MessageListView* MessageCenterViewTest::GetMessageListView() { return message_center_view_->message_list_view_.get(); } +FakeMessageCenterImpl* MessageCenterViewTest::GetMessageCenter() const { + return message_center_.get(); +} + NotificationView* MessageCenterViewTest::GetNotificationView( const std::string& id) { return message_center_view_->notification_views_[id]; @@ -236,7 +258,7 @@ void MessageCenterViewTest::AddNotification( scoped_ptr<Notification> notification) { std::string notification_id = notification->id(); notifications_.insert(notification.release()); - message_center_.SetVisibleNotifications(notifications_); + message_center_->SetVisibleNotifications(notifications_); message_center_view_->OnNotificationAdded(notification_id); } @@ -253,7 +275,7 @@ void MessageCenterViewTest::UpdateNotification( // |notifications| is a "set" container so we don't need to be aware the // order. notifications_.insert(notification.release()); - message_center_.SetVisibleNotifications(notifications_); + message_center_->SetVisibleNotifications(notifications_); message_center_view_->OnNotificationUpdated(notification_id); } @@ -267,7 +289,7 @@ void MessageCenterViewTest::RemoveNotification( break; } } - message_center_.SetVisibleNotifications(notifications_); + message_center_->SetVisibleNotifications(notifications_); message_center_view_->OnNotificationRemoved(notification_id, by_user); } @@ -322,6 +344,10 @@ void MessageCenterViewTest::LogBounds(int depth, views::View* view) { LogBounds(depth + 1, view->child_at(i)); } +MessageCenterButtonBar* MessageCenterViewTest::GetButtonBar() const { + return message_center_view_->button_bar_; +} + /* Unit tests *****************************************************************/ TEST_F(MessageCenterViewTest, CallTest) { @@ -488,4 +514,89 @@ TEST_F(MessageCenterViewTest, PositionAfterRemove) { GetMessageListView()->height()); } +TEST_F(MessageCenterViewTest, CloseButton) { + views::Button* close_button = GetButtonBar()->GetCloseAllButtonForTest(); + EXPECT_NE(nullptr, close_button); + + ((views::ButtonListener*)GetButtonBar()) + ->ButtonPressed(close_button, DummyEvent()); + base::MessageLoop::current()->Run(); + EXPECT_TRUE(GetMessageCenter()->remove_all_closable_notification_called_); +} + +TEST_F(MessageCenterViewTest, CloseButtonEnablity) { + views::Button* close_button = GetButtonBar()->GetCloseAllButtonForTest(); + EXPECT_NE(nullptr, close_button); + + // There should be 2 non-pinned notifications. + EXPECT_EQ(2u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_TRUE(close_button->enabled()); + + RemoveNotification(kNotificationId1, false); + base::MessageLoop::current()->Run(); + + // There should be 1 non-pinned notification. + EXPECT_EQ(1u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_TRUE(close_button->enabled()); + + RemoveNotification(kNotificationId2, false); + base::MessageLoop::current()->Run(); + + // There should be no notification. + EXPECT_EQ(0u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_FALSE(close_button->enabled()); + + Notification normal_notification( + NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId1), + base::UTF8ToUTF16("title2"), + base::UTF8ToUTF16("message\nwhich\nis\nvertically\nlong\n."), + gfx::Image(), base::UTF8ToUTF16("display source"), GURL(), + NotifierId(NotifierId::APPLICATION, "extension_id"), + message_center::RichNotificationData(), NULL); + +#if defined(OS_CHROMEOS) + Notification pinned_notification( + NOTIFICATION_TYPE_SIMPLE, std::string(kNotificationId2), + base::UTF8ToUTF16("title2"), + base::UTF8ToUTF16("message\nwhich\nis\nvertically\nlong\n."), + gfx::Image(), base::UTF8ToUTF16("display source"), GURL(), + NotifierId(NotifierId::APPLICATION, "extension_id"), + message_center::RichNotificationData(), NULL); + pinned_notification.set_pinned(true); + + AddNotification( + scoped_ptr<Notification>(new Notification(normal_notification))); + + // There should be 1 non-pinned notification. + EXPECT_EQ(1u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_TRUE(close_button->enabled()); + + AddNotification( + scoped_ptr<Notification>(new Notification(pinned_notification))); + + // There should be 1 normal notification and 1 pinned notification. + EXPECT_EQ(2u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_TRUE(close_button->enabled()); + + RemoveNotification(kNotificationId1, false); + + // There should be 1 pinned notification. + EXPECT_EQ(1u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_FALSE(close_button->enabled()); + + RemoveNotification(kNotificationId2, false); + + // There should be no notification. + EXPECT_EQ(0u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_FALSE(close_button->enabled()); + + AddNotification( + scoped_ptr<Notification>(new Notification(pinned_notification))); + + // There should be 1 pinned notification. + EXPECT_EQ(1u, GetMessageCenter()->GetVisibleNotifications().size()); + EXPECT_FALSE(close_button->enabled()); +#endif // defined(OS_CHROMEOS) +} + } // namespace message_center diff --git a/chromium/ui/message_center/views/message_list_view.cc b/chromium/ui/message_center/views/message_list_view.cc index 95933305a0e..70879983b38 100644 --- a/chromium/ui/message_center/views/message_list_view.cc +++ b/chromium/ui/message_center/views/message_list_view.cc @@ -191,17 +191,24 @@ void MessageListView::ResetRepositionSession() { fixed_height_ = 0; } -void MessageListView::ClearAllNotifications( +void MessageListView::ClearAllClosableNotifications( const gfx::Rect& visible_scroll_rect) { for (int i = 0; i < child_count(); ++i) { - views::View* child = child_at(i); + // Safe cast since all views in MessageListView are MessageViews. + MessageView* child = (MessageView*)child_at(i); if (!child->visible()) continue; if (gfx::IntersectRects(child->bounds(), visible_scroll_rect).IsEmpty()) continue; + if (child->IsPinned()) + continue; clearing_all_views_.push_back(child); } - DoUpdateIfPossible(); + if (clearing_all_views_.empty()) { + message_center_view()->OnAllNotificationsCleared(); + } else { + DoUpdateIfPossible(); + } } void MessageListView::OnBoundsAnimatorProgressed( diff --git a/chromium/ui/message_center/views/message_list_view.h b/chromium/ui/message_center/views/message_list_view.h index 2909f204f15..1007af3a000 100644 --- a/chromium/ui/message_center/views/message_list_view.h +++ b/chromium/ui/message_center/views/message_list_view.h @@ -46,7 +46,7 @@ class MessageListView : public views::View, void UpdateNotification(MessageView* view, const Notification& notification); void SetRepositionTarget(const gfx::Rect& target_rect); void ResetRepositionSession(); - void ClearAllNotifications(const gfx::Rect& visible_scroll_rect); + void ClearAllClosableNotifications(const gfx::Rect& visible_scroll_rect); MESSAGE_CENTER_EXPORT void SetRepositionTargetForTest( const gfx::Rect& target_rect); diff --git a/chromium/ui/message_center/views/message_popup_collection.cc b/chromium/ui/message_center/views/message_popup_collection.cc index 1a28ffe591e..e4280aa7871 100644 --- a/chromium/ui/message_center/views/message_popup_collection.cc +++ b/chromium/ui/message_center/views/message_popup_collection.cc @@ -83,7 +83,25 @@ void MessagePopupCollection::ClickOnNotification( void MessagePopupCollection::RemoveNotification( const std::string& notification_id, bool by_user) { - message_center_->RemoveNotification(notification_id, by_user); + NotificationList::PopupNotifications notifications = + message_center_->GetPopupNotifications(); + for (NotificationList::PopupNotifications::iterator iter = + notifications.begin(); + iter != notifications.end(); ++iter) { + Notification* notification = *iter; + DCHECK(notification); + + if (notification->id() != notification_id) + continue; + + // Don't remove the notification only when it's not pinned. + if (!notification->pinned()) + message_center_->RemoveNotification(notification_id, by_user); + else + message_center_->MarkSinglePopupAsShown(notification_id, true /* read */); + + break; + } } scoped_ptr<ui::MenuModel> MessagePopupCollection::CreateMenuModel( @@ -139,10 +157,21 @@ void MessagePopupCollection::UpdateWidgets() { if (FindToast((*iter)->id())) continue; - NotificationView* view = - NotificationView::Create(NULL, - *(*iter), - true); // Create top-level notification. + NotificationView* view; + // Create top-level notification. +#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 = NotificationView::Create(NULL, notification, true); + } else +#endif // defined(OS_CHROMEOS) + { + view = NotificationView::Create(NULL, *(*iter), true); + } + view->set_context_menu_controller(context_menu_controller_.get()); int view_height = ToastContentsView::GetToastSizeForView(view).height(); int height_available = diff --git a/chromium/ui/message_center/views/message_popup_collection_unittest.cc b/chromium/ui/message_center/views/message_popup_collection_unittest.cc index e38bb4b05a5..a41ae016cc2 100644 --- a/chromium/ui/message_center/views/message_popup_collection_unittest.cc +++ b/chromium/ui/message_center/views/message_popup_collection_unittest.cc @@ -118,7 +118,8 @@ class MessagePopupCollectionTest : public views::ViewsTestBase { void CloseAllToasts() { // Assumes there is at least one toast to close. EXPECT_TRUE(GetToastCounts() > 0); - MessageCenter::Get()->RemoveAllNotifications(false); + MessageCenter::Get()->RemoveAllNotifications( + false /* by_user */, MessageCenter::RemoveType::ALL); } gfx::Rect GetToastRectAt(size_t index) { @@ -480,6 +481,44 @@ TEST_F(MessagePopupCollectionTest, ManyPopupNotifications) { WaitForTransitionsDone(); } +#if defined(OS_CHROMEOS) + +TEST_F(MessagePopupCollectionTest, CloseNonClosableNotifications) { + const char* kNotificationId = "NOTIFICATION1"; + + scoped_ptr<Notification> notification(new Notification( + NOTIFICATION_TYPE_BASE_FORMAT, kNotificationId, + base::UTF8ToUTF16("test title"), base::UTF8ToUTF16("test message"), + gfx::Image(), base::string16() /* display_source */, GURL(), + NotifierId(NotifierId::APPLICATION, kNotificationId), + message_center::RichNotificationData(), new NotificationDelegate())); + notification->set_pinned(true); + + // Add a pinned notification. + MessageCenter::Get()->AddNotification(std::move(notification)); + WaitForTransitionsDone(); + + // Confirms that there is a toast. + EXPECT_EQ(1u, GetToastCounts()); + EXPECT_EQ(1u, MessageCenter::Get()->NotificationCount()); + + // Close the toast. + views::WidgetDelegateView* toast1 = GetToast(kNotificationId); + ASSERT_TRUE(toast1 != NULL); + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), 0, 0); + toast1->OnMouseEntered(event); + static_cast<MessageCenterObserver*>(collection()) + ->OnNotificationRemoved(kNotificationId, true); + WaitForTransitionsDone(); + + // Confirms that there is no toast. + EXPECT_EQ(0u, GetToastCounts()); + // But the notification still exists. + EXPECT_EQ(1u, MessageCenter::Get()->NotificationCount()); +} + +#endif // defined(OS_CHROMEOS) } // namespace test } // namespace message_center diff --git a/chromium/ui/message_center/views/message_view.cc b/chromium/ui/message_center/views/message_view.cc index 6f2597fb54c..80473515fad 100644 --- a/chromium/ui/message_center/views/message_view.cc +++ b/chromium/ui/message_center/views/message_view.cc @@ -26,9 +26,6 @@ namespace { -const int kCloseIconTopPadding = 5; -const int kCloseIconRightPadding = 5; - const int kShadowOffset = 1; const int kShadowBlur = 4; @@ -63,19 +60,6 @@ MessageView::MessageView(MessageViewController* controller, small_image_view->set_owned_by_client(); small_image_view_.reset(small_image_view); - PaddedButton *close = new PaddedButton(this); - close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding); - close->SetNormalImage(IDR_NOTIFICATION_CLOSE); - close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER); - close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED); - close->set_animate_on_state_change(false); - close->SetAccessibleName(l10n_util::GetStringUTF16( - IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); - // The close button should be added to view hierarchy by the derived class. - // This ensures that it is on top of other views. - close->set_owned_by_client(); - close_button_.reset(close); - focus_painter_ = views::Painter::CreateSolidFocusPainter( kFocusBorderColor, gfx::Insets(0, 1, 3, 2)); } @@ -103,12 +87,16 @@ void MessageView::CreateShadowBorder() { } bool MessageView::IsCloseButtonFocused() { - views::FocusManager* focus_manager = GetFocusManager(); - return focus_manager && focus_manager->GetFocusedView() == close_button(); + // May be overridden by the owner of the close button. + return false; } void MessageView::RequestFocusOnCloseButton() { - close_button_->RequestFocus(); + // May be overridden by the owner of the close button. +} + +bool MessageView::IsPinned() { + return false; } void MessageView::GetAccessibleState(ui::AXViewState* state) { @@ -151,7 +139,6 @@ bool MessageView::OnKeyReleased(const ui::KeyEvent& event) { } void MessageView::OnPaint(gfx::Canvas* canvas) { - DCHECK_EQ(this, close_button_->parent()); DCHECK_EQ(this, small_image_view_->parent()); SlideOutView::OnPaint(canvas); views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); @@ -175,14 +162,6 @@ void MessageView::Layout() { // Background. background_view_->SetBoundsRect(content_bounds); - // Close button. - gfx::Size close_size(close_button_->GetPreferredSize()); - gfx::Rect close_rect(content_bounds.right() - close_size.width(), - content_bounds.y(), - close_size.width(), - close_size.height()); - close_button_->SetBoundsRect(close_rect); - gfx::Size small_image_size(small_image_view_->GetPreferredSize()); gfx::Rect small_image_rect(small_image_size); small_image_rect.set_origin(gfx::Point( @@ -229,9 +208,6 @@ void MessageView::OnGestureEvent(ui::GestureEvent* event) { void MessageView::ButtonPressed(views::Button* sender, const ui::Event& event) { - if (sender == close_button()) { - controller_->RemoveNotification(notification_id_, true); // By user. - } } void MessageView::OnSlideOut() { diff --git a/chromium/ui/message_center/views/message_view.h b/chromium/ui/message_center/views/message_view.h index 47840df6b8f..6a4754e179c 100644 --- a/chromium/ui/message_center/views/message_view.h +++ b/chromium/ui/message_center/views/message_view.h @@ -46,8 +46,8 @@ const int kPaddingHorizontal = 18; const int kWebNotificationButtonWidth = 32; const int kWebNotificationIconSize = 40; -// An base class for a notification entry. Contains background, close button -// and other elements shared by derived notification views. +// An base class for a notification entry. Contains background and other +// elements shared by derived notification views. class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, public views::ButtonListener { public: @@ -67,8 +67,9 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, // Creates a shadow around the notification. void CreateShadowBorder(); - bool IsCloseButtonFocused(); - void RequestFocusOnCloseButton(); + virtual bool IsCloseButtonFocused(); + virtual void RequestFocusOnCloseButton(); + virtual bool IsPinned(); void set_accessible_name(const base::string16& accessible_name) { accessible_name_ = accessible_name; @@ -100,7 +101,6 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, void OnSlideOut() override; views::ImageView* small_image() { return small_image_view_.get(); } - views::ImageButton* close_button() { return close_button_.get(); } views::ScrollView* scroller() { return scroller_; } private: @@ -108,7 +108,6 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, std::string notification_id_; NotifierId notifier_id_; views::View* background_view_; // Owned by views hierarchy. - scoped_ptr<views::ImageButton> close_button_; scoped_ptr<views::ImageView> small_image_view_; views::ScrollView* scroller_; diff --git a/chromium/ui/message_center/views/notification_view.cc b/chromium/ui/message_center/views/notification_view.cc index fe91d1b0946..32aedfb2400 100644 --- a/chromium/ui/message_center/views/notification_view.cc +++ b/chromium/ui/message_center/views/notification_view.cc @@ -59,6 +59,9 @@ namespace { // Dimensions. const int kProgressBarBottomPadding = 0; +const int kCloseIconTopPadding = 5; +const int kCloseIconRightPadding = 5; + // static scoped_ptr<views::Border> MakeEmptyBorder(int top, int left, @@ -91,32 +94,6 @@ scoped_ptr<views::Border> MakeSeparatorBorder(int top, return views::Border::CreateSolidSidedBorder(top, left, 0, 0, color); } -// static -// Return true if and only if the image is null or has alpha. -bool HasAlpha(gfx::ImageSkia& image, views::Widget* widget) { - // Determine which bitmap to use. - float factor = 1.0f; - if (widget) - factor = ui::GetScaleFactorForNativeView(widget->GetNativeView()); - - // Extract that bitmap's alpha and look for a non-opaque pixel there. - SkBitmap bitmap = image.GetRepresentation(factor).sk_bitmap(); - if (!bitmap.isNull()) { - SkBitmap alpha; - bitmap.extractAlpha(&alpha); - for (int y = 0; y < bitmap.height(); ++y) { - for (int x = 0; x < bitmap.width(); ++x) { - if (alpha.getColor(x, y) != SK_ColorBLACK) { - return true; - } - } - } - } - - // If no opaque pixel was found, return false unless the bitmap is empty. - return bitmap.isNull(); -} - // ItemView //////////////////////////////////////////////////////////////////// // ItemViews are responsible for drawing each list notification item's title and @@ -228,7 +205,8 @@ views::View* NotificationView::TargetForRect(views::View* root, action_buttons_.end()); if (settings_button_view_) buttons.push_back(settings_button_view_); - buttons.push_back(close_button()); + if (close_button_) + buttons.push_back(close_button_.get()); for (size_t i = 0; i < buttons.size(); ++i) { gfx::Point point_in_child = point; @@ -241,6 +219,7 @@ views::View* NotificationView::TargetForRect(views::View* root, } void NotificationView::CreateOrUpdateViews(const Notification& notification) { + CreateOrUpdateCloseButtonView(notification); CreateOrUpdateTitleView(notification); CreateOrUpdateMessageView(notification); CreateOrUpdateProgressBarView(notification); @@ -308,7 +287,6 @@ NotificationView::NotificationView(MessageCenterController* controller, // image to overlap the content as needed to provide large enough click and // touch areas (<http://crbug.com/168822> and <http://crbug.com/168856>). AddChildView(small_image()); - AddChildView(close_button()); SetAccessibleName(notification); SetEventTargeter( @@ -349,13 +327,16 @@ int NotificationView::GetHeightForWidth(int width) const { } } - int content_height = std::max(top_height, kIconSize) + bottom_height; + int content_height = + std::max(top_height, kNotificationIconSize) + bottom_height; // Adjust the height to make sure there is at least 16px of space below the // icon if there is any space there (<http://crbug.com/232966>). - if (content_height > kIconSize) - content_height = std::max(content_height, - kIconSize + message_center::kIconBottomPadding); + if (content_height > kNotificationIconSize) { + content_height = + std::max(content_height, + kNotificationIconSize + message_center::kIconBottomPadding); + } return content_height + GetInsets().height(); } @@ -378,11 +359,22 @@ void NotificationView::Layout() { int top_height = top_view_->GetHeightForWidth(content_width); top_view_->SetBounds(insets.left(), insets.top(), content_width, top_height); + // Close button. + if (close_button_) { + gfx::Rect content_bounds = GetContentsBounds(); + gfx::Size close_size(close_button_->GetPreferredSize()); + gfx::Rect close_rect(content_bounds.right() - close_size.width(), + content_bounds.y(), close_size.width(), + close_size.height()); + close_button_->SetBoundsRect(close_rect); + } + // Icon. - icon_view_->SetBounds(insets.left(), insets.top(), kIconSize, kIconSize); + icon_view_->SetBounds(insets.left(), insets.top(), kNotificationIconSize, + kNotificationIconSize); // Settings & Bottom views. - int bottom_y = insets.top() + std::max(top_height, kIconSize); + int bottom_y = insets.top() + std::max(top_height, kNotificationIconSize); int bottom_height = bottom_view_->GetHeightForWidth(content_width); if (settings_button_view_) { @@ -445,6 +437,10 @@ void NotificationView::ButtonPressed(views::Button* sender, } } + if (close_button_ && sender == close_button_.get()) { + controller_->RemoveNotification(notification_id(), true); // By user. + } + // Let the superclass handle everything else. // Warning: This may cause the NotificationView itself to be deleted, // so don't do anything afterwards. @@ -533,12 +529,10 @@ base::string16 NotificationView::FormatContextMessage( if (notification.UseOriginAsContextMessage()) { const GURL url = notification.origin_url(); DCHECK(url.is_valid()); - // TODO(palmer): Find a way to get the Profile's real languages. - // crbug.com/496965. - return gfx::ElideText(url_formatter::FormatUrlForSecurityDisplayOmitScheme( - url, std::string()), - views::Label().font_list(), kContextMessageViewWidth, - gfx::ELIDE_HEAD); + return gfx::ElideText( + url_formatter::FormatUrlForSecurityDisplayOmitScheme(url), + views::Label().font_list(), kContextMessageViewWidth, + gfx::ELIDE_HEAD); } return gfx::TruncateString(notification.context_message(), @@ -663,24 +657,20 @@ void NotificationView::CreateOrUpdateListItemViews( void NotificationView::CreateOrUpdateIconView( const Notification& notification) { + gfx::Size image_view_size(kNotificationIconSize, kNotificationIconSize); + if (!icon_view_) { - icon_view_ = new ProportionalImageView(gfx::Size(kIconSize, kIconSize)); + icon_view_ = new ProportionalImageView(image_view_size); AddChildView(icon_view_); } gfx::ImageSkia icon = notification.icon().AsImageSkia(); - if (notification.adjust_icon()) { + icon_view_->SetImage(icon, icon.size()); + + if (notification.draw_icon_background()) { icon_view_->set_background( views::Background::CreateSolidBackground(kIconBackgroundColor)); - gfx::Size max_image_size = - notification.type() == NOTIFICATION_TYPE_SIMPLE && - (icon.width() < kIconSize || icon.height() < kIconSize || - HasAlpha(icon, GetWidget())) - ? gfx::Size(kLegacyIconSize, kLegacyIconSize) - : gfx::Size(kIconSize, kIconSize); - icon_view_->SetImage(icon, max_image_size); } else { - icon_view_->SetImage(icon, icon.size()); icon_view_->set_background(nullptr); } } @@ -772,6 +762,29 @@ void NotificationView::CreateOrUpdateActionButtonViews( } } +void NotificationView::CreateOrUpdateCloseButtonView( + const Notification& notification) { + set_slide_out_enabled(!notification.pinned()); + + if (!notification.pinned() && !close_button_) { + PaddedButton* close = new PaddedButton(this); + close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding); + close->SetNormalImage(IDR_NOTIFICATION_CLOSE); + close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER); + close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED); + close->set_animate_on_state_change(false); + close->SetAccessibleName(l10n_util::GetStringUTF16( + IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); + // The close button should be added to view hierarchy by the derived class. + // This ensures that it is on top of other views. + close->set_owned_by_client(); + close_button_.reset(close); + AddChildView(close_button_.get()); + } else if (notification.pinned() && close_button_) { + close_button_.reset(); + } +} + int NotificationView::GetMessageLineLimit(int title_lines, int width) const { // Image notifications require that the image must be kept flush against // their icons, but we can allow more text if no image. @@ -813,4 +826,22 @@ int NotificationView::GetMessageHeight(int width, int limit) const { message_view_->GetSizeForWidthAndLines(width, limit).height() : 0; } +bool NotificationView::IsCloseButtonFocused() { + if (!close_button_) + return false; + + views::FocusManager* focus_manager = GetFocusManager(); + return focus_manager && + focus_manager->GetFocusedView() == close_button_.get(); +} + +void NotificationView::RequestFocusOnCloseButton() { + if (close_button_) + close_button_->RequestFocus(); +} + +bool NotificationView::IsPinned() { + return !close_button_; +} + } // namespace message_center diff --git a/chromium/ui/message_center/views/notification_view.h b/chromium/ui/message_center/views/notification_view.h index 491080cfbeb..fd42a85879a 100644 --- a/chromium/ui/message_center/views/notification_view.h +++ b/chromium/ui/message_center/views/notification_view.h @@ -62,6 +62,9 @@ class MESSAGE_CENTER_EXPORT NotificationView // Overridden from MessageView: void UpdateWithNotification(const Notification& notification) override; void ButtonPressed(views::Button* sender, const ui::Event& event) override; + bool IsCloseButtonFocused() override; + void RequestFocusOnCloseButton() override; + bool IsPinned() override; // Overridden from MessageViewController: void ClickOnNotification(const std::string& notification_id) override; @@ -105,6 +108,7 @@ class MESSAGE_CENTER_EXPORT NotificationView void CreateOrUpdateIconView(const Notification& notification); void CreateOrUpdateImageView(const Notification& notification); void CreateOrUpdateActionButtonViews(const Notification& notification); + void CreateOrUpdateCloseButtonView(const Notification& notification); int GetMessageLineLimit(int title_lines, int width) const; int GetMessageHeight(int width, int limit) const; @@ -133,6 +137,7 @@ class MESSAGE_CENTER_EXPORT NotificationView ProportionalImageView* image_view_; NotificationProgressBarBase* progress_bar_view_; std::vector<NotificationButton*> action_buttons_; + scoped_ptr<views::ImageButton> close_button_; std::vector<views::View*> separators_; DISALLOW_COPY_AND_ASSIGN(NotificationView); diff --git a/chromium/ui/message_center/views/notification_view_unittest.cc b/chromium/ui/message_center/views/notification_view_unittest.cc index 383d1989dda..9be249c5569 100644 --- a/chromium/ui/message_center/views/notification_view_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_unittest.cc @@ -11,6 +11,7 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/events/event_processor.h" #include "ui/events/event_utils.h" #include "ui/gfx/canvas.h" @@ -31,6 +32,24 @@ #include "ui/views/test/widget_test.h" #include "ui/views/widget/widget_delegate.h" +namespace { + +scoped_ptr<ui::GestureEvent> GenerateGestureEvent(ui::EventType type) { + ui::GestureEventDetails detail(type); + scoped_ptr<ui::GestureEvent> event( + new ui::GestureEvent(0, 0, 0, base::TimeDelta(), detail)); + return event; +} + +scoped_ptr<ui::GestureEvent> GenerateGestureVerticalScrollUpdateEvent(int dx) { + ui::GestureEventDetails detail(ui::ET_GESTURE_SCROLL_UPDATE, dx, 0); + scoped_ptr<ui::GestureEvent> event( + new ui::GestureEvent(0, 0, 0, base::TimeDelta(), detail)); + return event; +} + +} // anonymouse namespace + namespace message_center { // A test delegate used for tests that deal with the notification settings @@ -54,7 +73,9 @@ class NotificationViewTest : public views::ViewsTestBase, void TearDown() override; views::Widget* widget() { return notification_view_->GetWidget(); } - NotificationView* notification_view() { return notification_view_.get(); } + NotificationView* notification_view() const { + return notification_view_.get(); + } Notification* notification() { return notification_.get(); } RichNotificationData* data() { return data_.get(); } @@ -155,12 +176,28 @@ class NotificationViewTest : public views::ViewsTestBase, } } + views::ImageButton* GetCloseButton() { + return notification_view()->close_button_.get(); + } + void UpdateNotificationViews() { notification_view()->CreateOrUpdateViews(*notification()); notification_view()->Layout(); } + float GetNotificationScrollAmount() const { + return notification_view()->GetTransform().To2dTranslation().x(); + } + + bool IsRemoved(const std::string& notification_id) const { + return (removed_ids_.find(notification_id) != removed_ids_.end()); + } + + void RemoveNotificationView() { notification_view_.reset(); } + private: + std::set<std::string> removed_ids_; + scoped_ptr<RichNotificationData> data_; scoped_ptr<Notification> notification_; scoped_ptr<NotificationView> notification_view_; @@ -216,8 +253,7 @@ void NotificationViewTest::ClickOnNotification( void NotificationViewTest::RemoveNotification( const std::string& notification_id, bool by_user) { - // For this test, this method should not be invoked. - NOTREACHED(); + removed_ids_.insert(notification_id); } scoped_ptr<ui::MenuModel> NotificationViewTest::CreateMenuModel( @@ -319,42 +355,33 @@ TEST_F(NotificationViewTest, TestIconSizing) { notification()->set_type(NOTIFICATION_TYPE_SIMPLE); ProportionalImageView* view = notification_view()->icon_view_; - // Icons smaller than the legacy size should be scaled up to it. - notification()->set_icon(CreateTestImage(kLegacyIconSize / 2, - kLegacyIconSize / 2)); + // Icons smaller than the maximum size should remain unscaled. + notification()->set_icon(CreateTestImage(kNotificationIconSize / 2, + kNotificationIconSize / 4)); UpdateNotificationViews(); - EXPECT_EQ(gfx::Size(kLegacyIconSize, kLegacyIconSize).ToString(), + EXPECT_EQ(gfx::Size(kNotificationIconSize / 2, + kNotificationIconSize / 4).ToString(), GetImagePaintSize(view).ToString()); - // Icons at the legacy size should be unscaled. - notification()->set_icon(CreateTestImage(kLegacyIconSize, kLegacyIconSize)); + // Icons of exactly the intended icon size should remain unscaled. + notification()->set_icon(CreateTestImage(kNotificationIconSize, + kNotificationIconSize)); UpdateNotificationViews(); - EXPECT_EQ(gfx::Size(kLegacyIconSize, kLegacyIconSize).ToString(), + EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(), GetImagePaintSize(view).ToString()); - // Icons slightly smaller than the preferred size should be scaled down to the - // legacy size to avoid having tiny borders (http://crbug.com/232966). - notification()->set_icon(CreateTestImage(kIconSize - 1, kIconSize - 1)); + // Icons over the maximum size should be scaled down, maintaining proportions. + notification()->set_icon(CreateTestImage(2 * kNotificationIconSize, + 2 * kNotificationIconSize)); UpdateNotificationViews(); - EXPECT_EQ(gfx::Size(kLegacyIconSize, kLegacyIconSize).ToString(), + EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(), GetImagePaintSize(view).ToString()); - // Icons at the preferred size or above should be scaled down to the preferred - // size. - notification()->set_icon(CreateTestImage(kIconSize, kIconSize)); + notification()->set_icon(CreateTestImage(4 * kNotificationIconSize, + 2 * kNotificationIconSize)); UpdateNotificationViews(); - EXPECT_EQ(gfx::Size(kIconSize, kIconSize).ToString(), - GetImagePaintSize(view).ToString()); - - notification()->set_icon(CreateTestImage(2 * kIconSize, 2 * kIconSize)); - UpdateNotificationViews(); - EXPECT_EQ(gfx::Size(kIconSize, kIconSize).ToString(), - GetImagePaintSize(view).ToString()); - - // Large, non-square images' aspect ratios should be preserved. - notification()->set_icon(CreateTestImage(4 * kIconSize, 2 * kIconSize)); - UpdateNotificationViews(); - EXPECT_EQ(gfx::Size(kIconSize, kIconSize / 2).ToString(), + EXPECT_EQ(gfx::Size(kNotificationIconSize, + kNotificationIconSize / 2).ToString(), GetImagePaintSize(view).ToString()); } @@ -618,4 +645,64 @@ TEST_F(NotificationViewTest, FormatContextMessageTest) { EXPECT_TRUE(base::UTF16ToUTF8(result).find("hello") == std::string::npos); } +TEST_F(NotificationViewTest, SlideOut) { + ui::ScopedAnimationDurationScaleMode zero_duration_scope( + ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); + + UpdateNotificationViews(); + std::string notification_id = notification()->id(); + + auto event_begin = GenerateGestureEvent(ui::ET_GESTURE_SCROLL_BEGIN); + auto event_scroll10 = GenerateGestureVerticalScrollUpdateEvent(-10); + auto event_scroll500 = GenerateGestureVerticalScrollUpdateEvent(-500); + auto event_end = GenerateGestureEvent(ui::ET_GESTURE_SCROLL_END); + + notification_view()->OnGestureEvent(event_begin.get()); + notification_view()->OnGestureEvent(event_scroll10.get()); + EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_EQ(-10.f, GetNotificationScrollAmount()); + notification_view()->OnGestureEvent(event_end.get()); + EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_EQ(0.f, GetNotificationScrollAmount()); + + notification_view()->OnGestureEvent(event_begin.get()); + notification_view()->OnGestureEvent(event_scroll500.get()); + EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_EQ(-500.f, GetNotificationScrollAmount()); + notification_view()->OnGestureEvent(event_end.get()); + EXPECT_TRUE(IsRemoved(notification_id)); +} + +// Pinning notification is ChromeOS only feature. +#if defined(OS_CHROMEOS) + +TEST_F(NotificationViewTest, SlideOutPinned) { + ui::ScopedAnimationDurationScaleMode zero_duration_scope( + ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); + + notification()->set_pinned(true); + UpdateNotificationViews(); + std::string notification_id = notification()->id(); + + auto event_begin = GenerateGestureEvent(ui::ET_GESTURE_SCROLL_BEGIN); + auto event_scroll500 = GenerateGestureVerticalScrollUpdateEvent(-500); + auto event_end = GenerateGestureEvent(ui::ET_GESTURE_SCROLL_END); + + notification_view()->OnGestureEvent(event_begin.get()); + notification_view()->OnGestureEvent(event_scroll500.get()); + EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_LT(-500.f, GetNotificationScrollAmount()); + notification_view()->OnGestureEvent(event_end.get()); + EXPECT_FALSE(IsRemoved(notification_id)); +} + +TEST_F(NotificationViewTest, Pinned) { + notification()->set_pinned(true); + + UpdateNotificationViews(); + EXPECT_EQ(NULL, GetCloseButton()); +} + +#endif // defined(OS_CHROMEOS) + } // namespace message_center diff --git a/chromium/ui/message_center/views/notifier_settings_view.cc b/chromium/ui/message_center/views/notifier_settings_view.cc index 145512d886a..bf80543124f 100644 --- a/chromium/ui/message_center/views/notifier_settings_view.cc +++ b/chromium/ui/message_center/views/notifier_settings_view.cc @@ -578,7 +578,7 @@ void NotifierSettingsView::UpdateContentsView( base::string16 notifier_group_text = active_group.login_info.empty() ? active_group.name : active_group.login_info; notifier_group_selector_ = - new views::MenuButton(NULL, notifier_group_text, this, true); + new views::MenuButton(notifier_group_text, this, true); notifier_group_selector_->SetBorder(scoped_ptr<views::Border>( new views::LabelButtonAssetBorder(views::Button::STYLE_BUTTON))); notifier_group_selector_->SetFocusPainter(nullptr); @@ -687,8 +687,9 @@ void NotifierSettingsView::ButtonPressed(views::Button* sender, provider_->SetNotifierEnabled((*iter)->notifier(), (*iter)->checked()); } -void NotifierSettingsView::OnMenuButtonClicked(views::View* source, - const gfx::Point& point) { +void NotifierSettingsView::OnMenuButtonClicked(views::MenuButton* source, + const gfx::Point& point, + const ui::Event* event) { notifier_group_menu_model_.reset(new NotifierGroupMenuModel(provider_)); notifier_group_menu_runner_.reset(new views::MenuRunner( notifier_group_menu_model_.get(), views::MenuRunner::CONTEXT_MENU)); diff --git a/chromium/ui/message_center/views/notifier_settings_view.h b/chromium/ui/message_center/views/notifier_settings_view.h index 1c635de0223..bbfa2fa7f98 100644 --- a/chromium/ui/message_center/views/notifier_settings_view.h +++ b/chromium/ui/message_center/views/notifier_settings_view.h @@ -107,8 +107,9 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView void ButtonPressed(views::Button* sender, const ui::Event& event) override; // Overridden from views::MenuButtonListener: - void OnMenuButtonClicked(views::View* source, - const gfx::Point& point) override; + void OnMenuButtonClicked(views::MenuButton* source, + const gfx::Point& point, + const ui::Event* event) override; views::ImageButton* title_arrow_; views::Label* title_label_; diff --git a/chromium/ui/message_center/views/toast_contents_view.cc b/chromium/ui/message_center/views/toast_contents_view.cc index a11b3b9874c..fd2cb46a50d 100644 --- a/chromium/ui/message_center/views/toast_contents_view.cc +++ b/chromium/ui/message_center/views/toast_contents_view.cc @@ -250,7 +250,7 @@ void ToastContentsView::OnDisplayChanged() { return; collection_->OnDisplayMetricsChanged( - Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view)); + Screen::GetScreen()->GetDisplayNearestWindow(native_view)); } void ToastContentsView::OnWorkAreaChanged() { @@ -263,7 +263,7 @@ void ToastContentsView::OnWorkAreaChanged() { return; collection_->OnDisplayMetricsChanged( - Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view)); + Screen::GetScreen()->GetDisplayNearestWindow(native_view)); } // views::View diff --git a/chromium/ui/metro_viewer/BUILD.gn b/chromium/ui/metro_viewer/BUILD.gn deleted file mode 100644 index 491ca7b4da8..00000000000 --- a/chromium/ui/metro_viewer/BUILD.gn +++ /dev/null @@ -1,19 +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. - -source_set("metro_viewer") { - sources = [ - "ime_types.cc", - "ime_types.h", - "metro_viewer_message_generator.cc", - "metro_viewer_message_generator.h", - "metro_viewer_messages.h", - ] - - deps = [ - "//base", - "//ipc", - "//ui/events", - ] -} diff --git a/chromium/ui/metro_viewer/DEPS b/chromium/ui/metro_viewer/DEPS deleted file mode 100644 index 8cb61f5e0a4..00000000000 --- a/chromium/ui/metro_viewer/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - "+ui/events", - "+ui/gfx", -] diff --git a/chromium/ui/metro_viewer/OWNERS b/chromium/ui/metro_viewer/OWNERS deleted file mode 100644 index 9b1c2450c48..00000000000 --- a/chromium/ui/metro_viewer/OWNERS +++ /dev/null @@ -1,12 +0,0 @@ -# Changes to IPC messages require a security review to avoid introducing -# new sandbox escapes. -per-file *_messages*.h=set noparent -per-file *_messages*.h=dcheng@chromium.org -per-file *_messages*.h=inferno@chromium.org -per-file *_messages*.h=jln@chromium.org -per-file *_messages*.h=jschuh@chromium.org -per-file *_messages*.h=kenrb@chromium.org -per-file *_messages*.h=mkwst@chromium.org -per-file *_messages*.h=nasko@chromium.org -per-file *_messages*.h=tsepez@chromium.org -per-file *_messages*.h=wfh@chromium.org diff --git a/chromium/ui/metro_viewer/ime_types.cc b/chromium/ui/metro_viewer/ime_types.cc deleted file mode 100644 index 9a37152fc99..00000000000 --- a/chromium/ui/metro_viewer/ime_types.cc +++ /dev/null @@ -1,30 +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 "ui/metro_viewer/ime_types.h" - -namespace metro_viewer { - -UnderlineInfo::UnderlineInfo() - : start_offset(0), - end_offset(0), - thick(false) { -} - -Composition::Composition() - : selection_start(0), - selection_end(0) { -} - -Composition::~Composition() { -} - -CharacterBounds::CharacterBounds() - : left(0), - top(0), - right(0), - bottom(0) { -} - -} // namespace metro_viewer diff --git a/chromium/ui/metro_viewer/ime_types.h b/chromium/ui/metro_viewer/ime_types.h deleted file mode 100644 index d37b40bae14..00000000000 --- a/chromium/ui/metro_viewer/ime_types.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_METRO_VIEWER_IME_TYPES_H_ -#define UI_METRO_VIEWER_IME_TYPES_H_ - -#include <stdint.h> - -#include <vector> - -#include "base/strings/string16.h" - -namespace metro_viewer { - -// An equivalent to ui::CompositionUnderline defined in -// "ui/base/ime/composition_underline.h". Redefined here to avoid dependency -// on ui.gyp from metro_driver.gyp. -struct UnderlineInfo { - UnderlineInfo(); - int32_t start_offset; - int32_t end_offset; - bool thick; -}; - -// An equivalent to ui::CompositionText defined in -// "ui/base/ime/composition_text.h". Redefined here to avoid dependency -// on ui.gyp from metro_driver.gyp. -struct Composition { - Composition(); - ~Composition(); - base::string16 text; - int32_t selection_start; - int32_t selection_end; - std::vector<UnderlineInfo> underlines; -}; - -// An equivalent to Win32 RECT structure. This can be gfx::Rect but redefined -// here to avoid dependency on gfx.gyp from metro_driver.gyp. -struct CharacterBounds { - CharacterBounds(); - int32_t left; - int32_t top; - int32_t right; - int32_t bottom; -}; - -} // namespace metro_viewer - -#endif // UI_METRO_VIEWER_IME_TYPES_H_ diff --git a/chromium/ui/metro_viewer/metro_viewer.gyp b/chromium/ui/metro_viewer/metro_viewer.gyp deleted file mode 100644 index c8bafdc0a61..00000000000 --- a/chromium/ui/metro_viewer/metro_viewer.gyp +++ /dev/null @@ -1,29 +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. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //ui/metro_viewer - 'target_name': 'metro_viewer_messages', - 'type': 'static_library', - 'dependencies': [ - '../../base/base.gyp:base', - ], - 'sources': [ - 'ime_types.cc', - 'ime_types.h', - 'metro_viewer_message_generator.cc', - 'metro_viewer_message_generator.h', - 'metro_viewer_messages.h', - ], - 'include_dirs': [ - '..', - ], - }, - ], -} diff --git a/chromium/ui/metro_viewer/metro_viewer_message_generator.cc b/chromium/ui/metro_viewer/metro_viewer_message_generator.cc deleted file mode 100644 index 360321ca240..00000000000 --- a/chromium/ui/metro_viewer/metro_viewer_message_generator.cc +++ /dev/null @@ -1,33 +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. - -// Get basic type definitions. -#define IPC_MESSAGE_IMPL -#include "ui/metro_viewer/metro_viewer_message_generator.h" - -// Generate constructors. -#include "ipc/struct_constructor_macros.h" -#include "ui/metro_viewer/metro_viewer_message_generator.h" - -// Generate destructors. -#include "ipc/struct_destructor_macros.h" -#include "ui/metro_viewer/metro_viewer_message_generator.h" - -// Generate param traits write methods. -#include "ipc/param_traits_write_macros.h" -namespace IPC { -#include "ui/metro_viewer/metro_viewer_message_generator.h" -} // namespace IPC - -// Generate param traits read methods. -#include "ipc/param_traits_read_macros.h" -namespace IPC { -#include "ui/metro_viewer/metro_viewer_message_generator.h" -} // namespace IPC - -// Generate param traits log methods. -#include "ipc/param_traits_log_macros.h" -namespace IPC { -#include "ui/metro_viewer/metro_viewer_message_generator.h" -} // namespace IPC diff --git a/chromium/ui/metro_viewer/metro_viewer_message_generator.h b/chromium/ui/metro_viewer/metro_viewer_message_generator.h deleted file mode 100644 index 4ea868838db..00000000000 --- a/chromium/ui/metro_viewer/metro_viewer_message_generator.h +++ /dev/null @@ -1,7 +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. - -// Multiply-included file, hence no include guard. - -#include "ui/metro_viewer/metro_viewer_messages.h" diff --git a/chromium/ui/metro_viewer/metro_viewer_messages.h b/chromium/ui/metro_viewer/metro_viewer_messages.h deleted file mode 100644 index 8dfab4dc771..00000000000 --- a/chromium/ui/metro_viewer/metro_viewer_messages.h +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 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. - -// Multiply-included message file, no include guard. - -#include <stdint.h> - -#include <vector> - -#include "ipc/ipc_message_macros.h" -#include "ui/events/event_constants.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/metro_viewer/ime_types.h" - -#define IPC_MESSAGE_START MetroViewerMsgStart - -IPC_ENUM_TRAITS(ui::EventType) -IPC_ENUM_TRAITS(ui::EventFlags) - -// Contains the parameters sent for a mousebutton message. -IPC_STRUCT_BEGIN(MetroViewerHostMsg_MouseButtonParams) - - IPC_STRUCT_MEMBER(int32_t, x) - IPC_STRUCT_MEMBER(int32_t, y) - IPC_STRUCT_MEMBER(int32_t, extra) - IPC_STRUCT_MEMBER(ui::EventType, event_type) - IPC_STRUCT_MEMBER(uint32_t, flags) - IPC_STRUCT_MEMBER(ui::EventFlags, changed_button) - IPC_STRUCT_MEMBER(bool, is_horizontal_wheel) - -IPC_STRUCT_END() - -// Messages sent from the viewer to the browser: - -// Inform the browser of the surface to target for compositing. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_SetTargetSurface, - gfx::NativeViewId /* target hwnd */, - float /* device scale */) -// Informs the browser that the mouse moved. -IPC_MESSAGE_CONTROL3(MetroViewerHostMsg_MouseMoved, - int32_t, /* x-coordinate */ - int32_t, /* y-coordinate */ - int32_t /* flags */) -// Informs the brower that a mouse button was pressed. -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_MouseButton, - MetroViewerHostMsg_MouseButtonParams) -// Informs the browser that a key was pressed. -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_KeyDown, - uint32_t, /* virtual key */ - uint32_t, /* repeat count */ - uint32_t, /* scan code */ - uint32_t /* key state */) -// Informs the browser that a key was released. -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_KeyUp, - uint32_t, /* virtual key */ - uint32_t, /* repeat count */ - uint32_t, /* scan code */ - uint32_t /* key state */) -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_Character, - uint32_t, /* virtual key */ - uint32_t, /* repeat count */ - uint32_t, /* scan code */ - uint32_t /* key state */) -// Informs the browser that the Metro window has been activated. -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_WindowActivated, - bool /* Whether the window should be repainted */) - -// Informs the browser that the user has completed an edge gesture. -IPC_MESSAGE_CONTROL0(MetroViewerHostMsg_EdgeGesture) - -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_TouchDown, - int32_t, /* x-coordinate */ - int32_t, /* y-coordinate */ - uint64_t, /* timestamp */ - uint32_t) /* pointer_id */ -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_TouchUp, - int32_t, /* x-coordinate */ - int32_t, /* y-coordinate */ - uint64_t, /* timestamp */ - uint32_t) /* pointer_id */ -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_TouchMoved, - int32_t, /* x-coordinate */ - int32_t, /* y-coordinate */ - uint64_t, /* timestamp */ - uint32_t) /* pointer_id */ - -// Informs the browser of the result of a file save as operation. -IPC_MESSAGE_CONTROL3(MetroViewerHostMsg_FileSaveAsDone, - bool, /* success */ - base::FilePath, /* filename */ - int) /* filter_index */ - -// Informs the browser of the result of a file open operation. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_FileOpenDone, - bool, /* success */ - base::FilePath) /* filename */ - -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_MultiFileOpenDone, - bool, /* success */ - std::vector<base::FilePath>) /* filenames */ - -// Informs the browser of the result of a select folder operation. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_SelectFolderDone, - bool, /* success */ - base::FilePath) /* filepath*/ - -// Messages sent from the browser to the viewer: - -// Requests the viewer to activate desktop mode. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_ActivateDesktop, - base::FilePath /* shortcut */, - bool /* ash exit */) - -// Request the viewer to close itself gracefully. -IPC_MESSAGE_CONTROL0(MetroViewerHostMsg_MetroExit) - -// Requests the viewer to open a URL in desktop mode. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_OpenURLOnDesktop, - base::FilePath /* shortcut */, - base::string16 /* url */) - -// Requests the viewer to change the pointer to a new cursor. -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_SetCursor, int64_t /* cursor */) - -// This structure contains the parameters sent to the viewer process to display -// the file save dialog. -IPC_STRUCT_BEGIN(MetroViewerHostMsg_SaveAsDialogParams) - - // The title of the file save dialog if any. - IPC_STRUCT_MEMBER(base::string16, title) - - // The suggested file name. - IPC_STRUCT_MEMBER(base::FilePath, suggested_name) - - // The save as filter to be used. - IPC_STRUCT_MEMBER(base::string16, filter) - - // The filter index. - IPC_STRUCT_MEMBER(uint32_t, filter_index) - - // The default extension. - IPC_STRUCT_MEMBER(base::string16, default_extension) - -IPC_STRUCT_END() - -// Requests the viewer to display the file save dialog. -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_DisplayFileSaveAs, - MetroViewerHostMsg_SaveAsDialogParams) - -// Requests the viewer to display the file open dialog. -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_DisplayFileOpen, - base::string16, /* title */ - base::string16, /* filter */ - base::FilePath, /* Default path */ - bool) /* allow_multi_select */ - -// Requests the viewer to display the select folder dialog. -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_DisplaySelectFolder, - base::string16) /* title */ - -// Sent to the viewer process to set the cursor position. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_SetCursorPos, - int, /* x */ - int) /* y */ - -// Ack sent by the viewer process indicating that the SetCursorPos operation -// was completed. -IPC_MESSAGE_CONTROL0(MetroViewerHostMsg_SetCursorPosAck) - -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_OpenURL, - base::string16) /* url */ - -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_SearchRequest, - base::string16) /* search_string */ - -// Sent from the metro viewer process to the browser process to indicate that -// the viewer window size has changed. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_WindowSizeChanged, - uint32_t, /* width */ - uint32_t) /* height */ - -IPC_STRUCT_TRAITS_BEGIN(metro_viewer::UnderlineInfo) - IPC_STRUCT_TRAITS_MEMBER(start_offset) - IPC_STRUCT_TRAITS_MEMBER(end_offset) - IPC_STRUCT_TRAITS_MEMBER(thick) -IPC_STRUCT_TRAITS_END() - -// Sent from the metro viewer process to the browser process to update the -// composition string. -IPC_MESSAGE_CONTROL4(MetroViewerHostMsg_ImeCompositionChanged, - base::string16, /* text */ - int32_t, /* selection_start */ - int32_t, /* selection_end */ - std::vector<metro_viewer::UnderlineInfo>) /* underlines */ - -// Sent from the metro viewer process to the browser process to update the -// status of popup window that is managed by an IME. -IPC_MESSAGE_CONTROL1( - MetroViewerHostMsg_ImeCandidatePopupChanged, - bool) /* UI visibility */ - -// Sent from the metro viewer process to the browser process to commit strings. -IPC_MESSAGE_CONTROL1(MetroViewerHostMsg_ImeTextCommitted, - base::string16) /* text */ - -// Sent from the metro viewer process to the browser process to notify that the -// active text input source is changed. -IPC_MESSAGE_CONTROL2(MetroViewerHostMsg_ImeInputSourceChanged, - uint16_t, /* Win32 LangID */ - bool) /* is IME or not */ - -// Requests the viewer to cancel the on-going composition. -IPC_MESSAGE_CONTROL0(MetroViewerHostMsg_ImeCancelComposition) - -IPC_STRUCT_TRAITS_BEGIN(metro_viewer::CharacterBounds) - IPC_STRUCT_TRAITS_MEMBER(left) - IPC_STRUCT_TRAITS_MEMBER(top) - IPC_STRUCT_TRAITS_MEMBER(right) - IPC_STRUCT_TRAITS_MEMBER(bottom) -IPC_STRUCT_TRAITS_END() - -// Requests the viewer to update the document context such as attached -// InputScopes and character bounds. -IPC_MESSAGE_CONTROL2( - MetroViewerHostMsg_ImeTextInputClientUpdated, - std::vector<int32_t>, /* InputScope enums */ - std::vector<metro_viewer::CharacterBounds>) /* character bounds */ diff --git a/chromium/ui/mojo/geometry/mojo_bindings.gyp b/chromium/ui/mojo/geometry/mojo_bindings.gyp index e5bfe67adee..28e1c98b296 100644 --- a/chromium/ui/mojo/geometry/mojo_bindings.gyp +++ b/chromium/ui/mojo/geometry/mojo_bindings.gyp @@ -13,14 +13,14 @@ 'geometry.mojom', ], }, - 'includes': [ '../../../third_party/mojo/mojom_bindings_generator_explicit.gypi' ], + 'includes': [ '../../../mojo/mojom_bindings_generator_explicit.gypi' ], }, { 'target_name': 'mojo_geometry_bindings', 'type': 'static_library', 'dependencies': [ 'mojo_geometry_bindings_mojom', - '../../../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', + '../../../mojo/mojo_public.gyp:mojo_cpp_bindings', ], }, ], diff --git a/chromium/ui/mojo/init/ui_init.cc b/chromium/ui/mojo/init/ui_init.cc index 7be35c749b1..34b0ae349e6 100644 --- a/chromium/ui/mojo/init/ui_init.cc +++ b/chromium/ui/mojo/init/ui_init.cc @@ -43,7 +43,7 @@ class GestureConfigurationMojo : public ui::GestureConfiguration { UIInit::UIInit(const std::vector<gfx::Display>& displays) : screen_(new ScreenMojo(displays)) { - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get()); + gfx::Screen::SetScreenInstance(screen_.get()); #if defined(OS_ANDROID) gesture_configuration_.reset(new GestureConfigurationMojo); ui::GestureConfiguration::SetInstance(gesture_configuration_.get()); @@ -51,7 +51,7 @@ UIInit::UIInit(const std::vector<gfx::Display>& displays) } UIInit::~UIInit() { - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, nullptr); + gfx::Screen::SetScreenInstance(nullptr); #if defined(OS_ANDROID) ui::GestureConfiguration::SetInstance(nullptr); #endif diff --git a/chromium/ui/native_theme/common_theme.cc b/chromium/ui/native_theme/common_theme.cc index d2f808065ae..ab72df6dd66 100644 --- a/chromium/ui/native_theme/common_theme.cc +++ b/chromium/ui/native_theme/common_theme.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" @@ -29,11 +29,6 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, if (ui::MaterialDesignController::IsModeMaterial()) { // Dialogs: static const SkColor kDialogBackgroundColorMd = SK_ColorWHITE; - // Button: - static const SkColor kTextButtonEnabledColorMd = - SkColorSetRGB(0x64, 0x64, 0x64); - static const SkColor kTextButtonDisabledColorMd = - SkColorSetA(kTextButtonEnabledColorMd, 0x80); // MenuItem: static const SkColor kMenuHighlightBackgroundColorMd = SkColorSetARGB(0x14, 0x00, 0x00, 0x00); @@ -54,12 +49,6 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, case NativeTheme::kColorId_BubbleBackground: return kDialogBackgroundColorMd; - // Button - case NativeTheme::kColorId_MdTextButtonEnabledColor: - return kTextButtonEnabledColorMd; - case NativeTheme::kColorId_MdTextButtonDisabledColor: - return kTextButtonDisabledColorMd; - // MenuItem case NativeTheme::kColorId_FocusedMenuItemBackgroundColor: return kMenuHighlightBackgroundColorMd; @@ -132,6 +121,7 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, static const SkColor kBlueButtonHoverColor = SK_ColorWHITE; static const SkColor kBlueButtonShadowColor = SkColorSetRGB(0x53, 0x8C, 0xEA); static const SkColor kCallToActionColor = gfx::kGoogleBlue500; + static const SkColor kTextOnCallToActionColor = SK_ColorWHITE; // MenuItem: static const SkColor kMenuBackgroundColor = SK_ColorWHITE; static const SkColor kMenuHighlightBackgroundColor = @@ -263,6 +253,8 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, return kBlueButtonShadowColor; case NativeTheme::kColorId_CallToActionColor: return kCallToActionColor; + case NativeTheme::kColorId_TextOnCallToActionColor: + return kTextOnCallToActionColor; // MenuItem case NativeTheme::kColorId_MenuBorderColor: @@ -405,8 +397,6 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, case NativeTheme::kColorId_ThrobberLightColor: return kThrobberLightColor; - case NativeTheme::kColorId_MdTextButtonEnabledColor: - case NativeTheme::kColorId_MdTextButtonDisabledColor: case NativeTheme::kColorId_NumColors: break; } @@ -415,32 +405,12 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, return gfx::kPlaceholderColor; } -gfx::Size CommonThemeGetPartSize(NativeTheme::Part part, - NativeTheme::State state, - const NativeTheme::ExtraParams& extra) { - gfx::Size size; - switch (part) { - case NativeTheme::kComboboxArrow: - return ui::ResourceBundle::GetSharedInstance().GetImageNamed( - IDR_MENU_DROPARROW).Size(); - - default: - break; - } - - return size; -} - -void CommonThemePaintComboboxArrow(SkCanvas* canvas, const gfx::Rect& rect) { - gfx::ImageSkia* arrow = ui::ResourceBundle::GetSharedInstance(). - GetImageSkiaNamed(IDR_MENU_DROPARROW); - CommonThemeCreateCanvas(canvas)->DrawImageInt(*arrow, rect.x(), rect.y()); -} - -void CommonThemePaintMenuItemBackground(const NativeTheme *theme, - SkCanvas* canvas, - NativeTheme::State state, - const gfx::Rect &rect) { +void CommonThemePaintMenuItemBackground( + const NativeTheme* theme, + SkCanvas* canvas, + NativeTheme::State state, + const gfx::Rect& rect, + const NativeTheme::MenuItemExtraParams& menu_item) { SkPaint paint; switch (state) { case NativeTheme::kNormal: @@ -456,6 +426,11 @@ void CommonThemePaintMenuItemBackground(const NativeTheme *theme, NOTREACHED() << "Invalid state " << state; break; } + if (menu_item.corner_radius > 0) { + const SkScalar radius = SkIntToScalar(menu_item.corner_radius); + canvas->drawRoundRect(gfx::RectToSkRect(rect), radius, radius, paint); + return; + } canvas->drawRect(gfx::RectToSkRect(rect), paint); } @@ -465,7 +440,8 @@ scoped_ptr<gfx::Canvas> CommonThemeCreateCanvas(SkCanvas* sk_canvas) { // scale factor from canvas scale. SkMatrix m = sk_canvas->getTotalMatrix(); float device_scale = static_cast<float>(SkScalarAbs(m.getScaleX())); - return make_scoped_ptr(new gfx::Canvas(sk_canvas, device_scale)); + return make_scoped_ptr(new gfx::Canvas(skia::SharePtr(sk_canvas), + device_scale)); } } // namespace ui diff --git a/chromium/ui/native_theme/common_theme.h b/chromium/ui/native_theme/common_theme.h index 87c5eca78af..48edf85561f 100644 --- a/chromium/ui/native_theme/common_theme.h +++ b/chromium/ui/native_theme/common_theme.h @@ -24,20 +24,12 @@ namespace ui { SkColor NATIVE_THEME_EXPORT GetAuraColor(NativeTheme::ColorId color_id, const NativeTheme* base_theme); -gfx::Size NATIVE_THEME_EXPORT CommonThemeGetPartSize( - NativeTheme::Part part, - NativeTheme::State state, - const NativeTheme::ExtraParams& extra); - -void NATIVE_THEME_EXPORT CommonThemePaintComboboxArrow( - SkCanvas* canvas, - const gfx::Rect& rect); - void NATIVE_THEME_EXPORT CommonThemePaintMenuItemBackground( const NativeTheme* theme, SkCanvas* canvas, NativeTheme::State state, - const gfx::Rect& rect); + const gfx::Rect& rect, + const NativeTheme::MenuItemExtraParams& menu_item); // Creates a gfx::Canvas wrapping an SkCanvas. scoped_ptr<gfx::Canvas> NATIVE_THEME_EXPORT CommonThemeCreateCanvas( diff --git a/chromium/ui/native_theme/native_theme.cc b/chromium/ui/native_theme/native_theme.cc index f15f144b8c0..e4911c97d01 100644 --- a/chromium/ui/native_theme/native_theme.cc +++ b/chromium/ui/native_theme/native_theme.cc @@ -4,10 +4,20 @@ #include "ui/native_theme/native_theme.h" +#include <cstring> + #include "ui/native_theme/native_theme_observer.h" namespace ui { +NativeTheme::ExtraParams::ExtraParams() { + memset(this, 0, sizeof(*this)); +} + +NativeTheme::ExtraParams::ExtraParams(const ExtraParams& other) { + memcpy(this, &other, sizeof(*this)); +} + void NativeTheme::SetScrollbarColors(unsigned inactive_color, unsigned active_color, unsigned track_color) { diff --git a/chromium/ui/native_theme/native_theme.h b/chromium/ui/native_theme/native_theme.h index b09dd15b1c1..917ed956c28 100644 --- a/chromium/ui/native_theme/native_theme.h +++ b/chromium/ui/native_theme/native_theme.h @@ -44,7 +44,6 @@ class NATIVE_THEME_EXPORT NativeTheme { public: // The part to be painted / sized. enum Part { - kComboboxArrow, kCheckbox, kInnerSpinButton, kMenuList, @@ -132,6 +131,7 @@ class NATIVE_THEME_EXPORT NativeTheme { struct MenuItemExtraParams { bool is_selected; + int corner_radius; }; struct MenuListExtraParams { @@ -196,7 +196,10 @@ class NATIVE_THEME_EXPORT NativeTheme { int classic_state; // Used on Windows when uxtheme is not available. }; - union ExtraParams { + union NATIVE_THEME_EXPORT ExtraParams { + ExtraParams(); + ExtraParams(const ExtraParams& other); + ButtonExtraParams button; InnerSpinButtonExtraParams inner_spin; MenuArrowExtraParams menu_arrow; @@ -262,8 +265,7 @@ class NATIVE_THEME_EXPORT NativeTheme { kColorId_BlueButtonHoverColor, kColorId_BlueButtonShadowColor, kColorId_CallToActionColor, - kColorId_MdTextButtonEnabledColor, - kColorId_MdTextButtonDisabledColor, + kColorId_TextOnCallToActionColor, // MenuItem kColorId_EnabledMenuItemForegroundColor, kColorId_DisabledMenuItemForegroundColor, diff --git a/chromium/ui/native_theme/native_theme_aura.cc b/chromium/ui/native_theme/native_theme_aura.cc index ffb4347a83c..74edcd558c8 100644 --- a/chromium/ui/native_theme/native_theme_aura.cc +++ b/chromium/ui/native_theme/native_theme_aura.cc @@ -10,7 +10,7 @@ #include "base/logging.h" #include "build/build_config.h" #include "ui/base/layout.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/animation/tween.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" @@ -130,8 +130,8 @@ void NativeThemeAura::PaintMenuItemBackground( SkCanvas* canvas, State state, const gfx::Rect& rect, - const MenuListExtraParams& menu_list) const { - CommonThemePaintMenuItemBackground(this, canvas, state, rect); + const MenuItemExtraParams& menu_item) const { + CommonThemePaintMenuItemBackground(this, canvas, state, rect, menu_item); } void NativeThemeAura::PaintArrowButton(SkCanvas* canvas, diff --git a/chromium/ui/native_theme/native_theme_aura.h b/chromium/ui/native_theme/native_theme_aura.h index d9503f6ab2e..85ce5bc708d 100644 --- a/chromium/ui/native_theme/native_theme_aura.h +++ b/chromium/ui/native_theme/native_theme_aura.h @@ -29,7 +29,7 @@ class NATIVE_THEME_EXPORT NativeThemeAura : public NativeThemeBase { SkCanvas* canvas, State state, const gfx::Rect& rect, - const MenuListExtraParams& menu_list) const override; + const MenuItemExtraParams& menu_item) const override; void PaintArrowButton(SkCanvas* gc, const gfx::Rect& rect, Part direction, diff --git a/chromium/ui/native_theme/native_theme_aurawin.cc b/chromium/ui/native_theme/native_theme_aurawin.cc index 956186b535c..03e53c65fdb 100644 --- a/chromium/ui/native_theme/native_theme_aurawin.cc +++ b/chromium/ui/native_theme/native_theme_aurawin.cc @@ -72,10 +72,6 @@ void NativeThemeAuraWin::Paint(SkCanvas* canvas, gfx::Size NativeThemeAuraWin::GetPartSize(Part part, State state, const ExtraParams& extra) const { - gfx::Size part_size = CommonThemeGetPartSize(part, state, extra); - if (!part_size.IsEmpty()) - return part_size; - // We want aura on windows to use the same size for scrollbars as we would in // the native theme. if (IsScrollbarPart(part)) diff --git a/chromium/ui/native_theme/native_theme_base.cc b/chromium/ui/native_theme/native_theme_base.cc index 1a7837ef3eb..d0f135ac750 100644 --- a/chromium/ui/native_theme/native_theme_base.cc +++ b/chromium/ui/native_theme/native_theme_base.cc @@ -97,10 +97,6 @@ namespace ui { gfx::Size NativeThemeBase::GetPartSize(Part part, State state, const ExtraParams& extra) const { - gfx::Size size = CommonThemeGetPartSize(part, state, extra); - if (!size.IsEmpty()) - return size; - switch (part) { // Please keep these in the order of NativeTheme::Part. case kCheckbox: @@ -197,9 +193,6 @@ void NativeThemeBase::Paint(SkCanvas* canvas, switch (part) { // Please keep these in the order of NativeTheme::Part. - case kComboboxArrow: - CommonThemePaintComboboxArrow(canvas, rect); - break; case kCheckbox: PaintCheckbox(canvas, state, rect, extra.button); break; @@ -213,7 +206,7 @@ void NativeThemeBase::Paint(SkCanvas* canvas, PaintMenuPopupBackground(canvas, rect.size(), extra.menu_background); break; case kMenuItemBackground: - PaintMenuItemBackground(canvas, state, rect, extra.menu_list); + PaintMenuItemBackground(canvas, state, rect, extra.menu_item); break; case kProgressBar: PaintProgressBar(canvas, state, rect, extra.progress_bar); @@ -616,12 +609,10 @@ SkRect NativeThemeBase::PaintCheckboxRadioCommon( else /* kNormal */ startEndColors = kCheckboxGradientColors; SkColor colors[3] = {startEndColors[0], startEndColors[0], startEndColors[1]}; - skia::RefPtr<SkShader> shader = skia::AdoptRef( - SkGradientShader::CreateLinear( - gradient_bounds, colors, NULL, 3, SkShader::kClamp_TileMode)); SkPaint paint; paint.setAntiAlias(true); - paint.setShader(shader.get()); + paint.setShader(SkGradientShader::MakeLinear(gradient_bounds, colors, NULL, 3, + SkShader::kClamp_TileMode)); paint.setStyle(SkPaint::kFill_Style); canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint); paint.setShader(NULL); @@ -700,12 +691,11 @@ void NativeThemeBase::PaintButton(SkCanvas* canvas, std::swap(gradient_bounds[0], gradient_bounds[1]); SkColor colors[2] = { light_color, base_color }; - skia::RefPtr<SkShader> shader = skia::AdoptRef( - SkGradientShader::CreateLinear( - gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode)); paint.setStyle(SkPaint::kFill_Style); paint.setAntiAlias(true); - paint.setShader(shader.get()); + paint.setShader( + SkGradientShader::MakeLinear( + gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode)); canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint); paint.setShader(NULL); @@ -792,7 +782,7 @@ void NativeThemeBase::PaintMenuItemBackground( SkCanvas* canvas, State state, const gfx::Rect& rect, - const MenuListExtraParams& menu_list) const { + const MenuItemExtraParams& menu_item) const { // By default don't draw anything over the normal background. } diff --git a/chromium/ui/native_theme/native_theme_base.h b/chromium/ui/native_theme/native_theme_base.h index 5b91753426c..a8677d3bf30 100644 --- a/chromium/ui/native_theme/native_theme_base.h +++ b/chromium/ui/native_theme/native_theme_base.h @@ -108,7 +108,7 @@ class NATIVE_THEME_EXPORT NativeThemeBase : public NativeTheme { SkCanvas* canvas, State state, const gfx::Rect& rect, - const MenuListExtraParams& menu_list) const; + const MenuItemExtraParams& menu_item) const; virtual void PaintSliderTrack( SkCanvas* canvas, diff --git a/chromium/ui/native_theme/native_theme_dark_aura.cc b/chromium/ui/native_theme/native_theme_dark_aura.cc index 33392455691..b00d32ff7c9 100644 --- a/chromium/ui/native_theme/native_theme_dark_aura.cc +++ b/chromium/ui/native_theme/native_theme_dark_aura.cc @@ -4,7 +4,7 @@ #include "ui/native_theme/native_theme_dark_aura.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/color_palette.h" namespace ui { @@ -67,6 +67,8 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { return kResultsTableDimmedText; // Intentional pass-throughs to NativeThemeAura. + case kColorId_ButtonEnabledColor: + case kColorId_TextOnCallToActionColor: case kColorId_ResultsTableHoveredBackground: case kColorId_ResultsTableSelectedBackground: case kColorId_ResultsTableNormalUrl: @@ -75,9 +77,70 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { return NativeThemeAura::GetSystemColor(color_id); // Any other color is not defined and shouldn't be used in a dark theme. - default: + case kColorId_WindowBackground: + case kColorId_DialogBackground: + case kColorId_BubbleBackground: + case kColorId_FocusedBorderColor: + case kColorId_UnfocusedBorderColor: + case kColorId_ButtonBackgroundColor: + case kColorId_ButtonDisabledColor: + case kColorId_ButtonHighlightColor: + case kColorId_ButtonHoverColor: + case kColorId_ButtonHoverBackgroundColor: + case kColorId_BlueButtonEnabledColor: + case kColorId_BlueButtonDisabledColor: + case kColorId_BlueButtonPressedColor: + case kColorId_BlueButtonHoverColor: + case kColorId_BlueButtonShadowColor: + case kColorId_EnabledMenuItemForegroundColor: + case kColorId_DisabledMenuItemForegroundColor: + case kColorId_DisabledEmphasizedMenuItemForegroundColor: + case kColorId_SelectedMenuItemForegroundColor: + case kColorId_FocusedMenuItemBackgroundColor: + case kColorId_HoverMenuItemBackgroundColor: + case kColorId_MenuSeparatorColor: + case kColorId_MenuBackgroundColor: + case kColorId_MenuBorderColor: + case kColorId_EnabledMenuButtonBorderColor: + case kColorId_FocusedMenuButtonBorderColor: + case kColorId_HoverMenuButtonBorderColor: + case kColorId_LabelEnabledColor: + case kColorId_LabelDisabledColor: + case kColorId_LabelBackgroundColor: + case kColorId_LinkDisabled: + case kColorId_TextfieldReadOnlyColor: + case kColorId_TextfieldReadOnlyBackground: + case kColorId_TooltipBackground: + case kColorId_TooltipText: + case kColorId_TreeBackground: + case kColorId_TreeText: + case kColorId_TreeSelectedText: + case kColorId_TreeSelectedTextUnfocused: + case kColorId_TreeSelectionBackgroundFocused: + case kColorId_TreeSelectionBackgroundUnfocused: + case kColorId_TreeArrow: + case kColorId_TableBackground: + case kColorId_TableText: + case kColorId_TableSelectedText: + case kColorId_TableSelectedTextUnfocused: + case kColorId_TableSelectionBackgroundFocused: + case kColorId_TableSelectionBackgroundUnfocused: + case kColorId_TableGroupingIndicatorColor: + case kColorId_ResultsTablePositiveText: + case kColorId_ResultsTablePositiveHoveredText: + case kColorId_ResultsTablePositiveSelectedText: + case kColorId_ResultsTableNegativeText: + case kColorId_ResultsTableNegativeHoveredText: + case kColorId_ResultsTableNegativeSelectedText: + case kColorId_ThrobberSpinningColor: + case kColorId_ThrobberWaitingColor: + case kColorId_ThrobberLightColor: + case kColorId_NumColors: return gfx::kPlaceholderColor; } + + NOTREACHED(); + return gfx::kPlaceholderColor; } NativeThemeDarkAura::NativeThemeDarkAura() {} diff --git a/chromium/ui/native_theme/native_theme_dark_win.cc b/chromium/ui/native_theme/native_theme_dark_win.cc index d2511434926..c79f51eb741 100644 --- a/chromium/ui/native_theme/native_theme_dark_win.cc +++ b/chromium/ui/native_theme/native_theme_dark_win.cc @@ -4,7 +4,7 @@ #include "ui/native_theme/native_theme_dark_win.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/native_theme/native_theme_dark_aura.h" namespace ui { diff --git a/chromium/ui/native_theme/native_theme_mac.h b/chromium/ui/native_theme/native_theme_mac.h index 7970a586f2b..43071749bb7 100644 --- a/chromium/ui/native_theme/native_theme_mac.h +++ b/chromium/ui/native_theme/native_theme_mac.h @@ -14,24 +14,14 @@ namespace ui { // Mac implementation of native theme support. class NATIVE_THEME_EXPORT NativeThemeMac : public NativeThemeBase { public: + static const int kComboboxCornerRadius = 5; + static NativeThemeMac* instance(); // Overridden from NativeTheme: SkColor GetSystemColor(ColorId color_id) const override; // Overridden from NativeThemeBase: - void PaintScrollbarTrack(SkCanvas* canvas, - Part part, - State state, - const ScrollbarTrackExtraParams& extra_params, - const gfx::Rect& rect) const override; - void PaintScrollbarThumb(SkCanvas* sk_canvas, - Part part, - State state, - const gfx::Rect& rect) const override; - void PaintScrollbarCorner(SkCanvas* canvas, - State state, - const gfx::Rect& rect) const override; void PaintMenuPopupBackground( SkCanvas* canvas, const gfx::Size& size, @@ -40,7 +30,10 @@ class NATIVE_THEME_EXPORT NativeThemeMac : public NativeThemeBase { SkCanvas* canvas, State state, const gfx::Rect& rect, - const MenuListExtraParams& menu_list) const override; + const MenuItemExtraParams& menu_item) const override; + + // Creates a shader appropriate for painting the background of a button. + static sk_sp<SkShader> GetButtonBackgroundShader(State state, int height); private: NativeThemeMac(); diff --git a/chromium/ui/native_theme/native_theme_mac.mm b/chromium/ui/native_theme/native_theme_mac.mm index 43eb1296316..88343f74db2 100644 --- a/chromium/ui/native_theme/native_theme_mac.mm +++ b/chromium/ui/native_theme/native_theme_mac.mm @@ -13,38 +13,26 @@ #include "base/macros.h" #import "skia/ext/skia_utils_mac.h" #include "third_party/skia/include/effects/SkGradientShader.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/skia_util.h" #include "ui/native_theme/common_theme.h" namespace { -const SkColor kScrollerTrackGradientColors[] = { - SkColorSetRGB(0xEF, 0xEF, 0xEF), - SkColorSetRGB(0xF9, 0xF9, 0xF9), - SkColorSetRGB(0xFD, 0xFD, 0xFD), - SkColorSetRGB(0xF6, 0xF6, 0xF6) }; -const SkColor kScrollerTrackInnerBorderColor = SkColorSetRGB(0xE4, 0xE4, 0xE4); -const SkColor kScrollerTrackOuterBorderColor = SkColorSetRGB(0xEF, 0xEF, 0xEF); -const SkColor kScrollerThumbColor = SkColorSetARGB(0x38, 0, 0, 0); -const SkColor kScrollerThumbHoverColor = SkColorSetARGB(0x80, 0, 0, 0); -const int kScrollerTrackBorderWidth = 1; - -// The amount the thumb is inset from both the ends and the sides of the track. -const int kScrollerThumbInset = 3; - // Values calculated by reading pixels and solving simultaneous equations // derived from "A over B" alpha compositing. Steps: Sample the semi-transparent // pixel over two backgrounds; P1, P2 over backgrounds B1, B2. Use the color // value between 0.0 and 1.0 (i.e. divide by 255.0). Then, // alpha = (P2 - P1 + B1 - B2) / (B1 - B2) // color = (P1 - B1 + alpha * B1) / alpha. -const SkColor kMenuPopupBackgroundColor = SkColorSetARGB(255, 255, 255, 255); -const SkColor kMenuSeparatorColor = SkColorSetARGB(243, 228, 228, 228); +const SkColor kMenuPopupBackgroundColor = SkColorSetARGB(245, 255, 255, 255); +const SkColor kMenuSeparatorColor = SkColorSetARGB(255, 217, 217, 217); const SkColor kMenuBorderColor = SkColorSetARGB(60, 0, 0, 0); -const SkColor kMenuPopupBackgroundColorYosemite = - SkColorSetARGB(255, 230, 230, 230); +const SkColor kMenuPopupBackgroundColorMavericks = + SkColorSetARGB(255, 255, 255, 255); +const SkColor kMenuSeparatorColorMavericks = SkColorSetARGB(243, 228, 228, 228); // Hardcoded color used for some existing dialogs in Chrome's Cocoa UI. const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251); @@ -185,7 +173,8 @@ SkColor NativeThemeMac::GetSystemColor(ColorId color_id) const { case kColorId_MenuBackgroundColor: return kMenuPopupBackgroundColor; case kColorId_MenuSeparatorColor: - return kMenuSeparatorColor; + return base::mac::IsOSMavericks() ? kMenuSeparatorColorMavericks + : kMenuSeparatorColor; case kColorId_MenuBorderColor: return kMenuBorderColor; @@ -240,139 +229,14 @@ SkColor NativeThemeMac::GetSystemColor(ColorId color_id) const { } } -void NativeThemeMac::PaintScrollbarTrack( - SkCanvas* canvas, - Part part, - State state, - const ScrollbarTrackExtraParams& extra_params, - const gfx::Rect& rect) const { - // Emulate the non-overlay scroller style from OSX 10.7 and later. - SkPoint gradient_bounds[2]; - if (part == kScrollbarVerticalTrack) { - gradient_bounds[0].set(rect.x(), rect.y()); - gradient_bounds[1].set(rect.right(), rect.y()); - } else { - DCHECK_EQ(part, kScrollbarHorizontalTrack); - gradient_bounds[0].set(rect.x(), rect.y()); - gradient_bounds[1].set(rect.x(), rect.bottom()); - } - skia::RefPtr<SkShader> shader = skia::AdoptRef( - SkGradientShader::CreateLinear(gradient_bounds, - kScrollerTrackGradientColors, - NULL, - arraysize(kScrollerTrackGradientColors), - SkShader::kClamp_TileMode)); - SkPaint gradient; - gradient.setShader(shader.get()); - - SkIRect track_rect = gfx::RectToSkIRect(rect); - canvas->drawIRect(track_rect, gradient); - - // Draw inner and outer line borders. - if (part == kScrollbarVerticalTrack) { - SkPaint paint; - paint.setColor(kScrollerTrackInnerBorderColor); - canvas->drawRectCoords(track_rect.left(), - track_rect.top(), - track_rect.left() + kScrollerTrackBorderWidth, - track_rect.bottom(), - paint); - paint.setColor(kScrollerTrackOuterBorderColor); - canvas->drawRectCoords(track_rect.right() - kScrollerTrackBorderWidth, - track_rect.top(), - track_rect.right(), - track_rect.bottom(), - paint); - } else { - SkPaint paint; - paint.setColor(kScrollerTrackInnerBorderColor); - canvas->drawRectCoords(track_rect.left(), - track_rect.top(), - track_rect.right(), - track_rect.top() + kScrollerTrackBorderWidth, - paint); - paint.setColor(kScrollerTrackOuterBorderColor); - canvas->drawRectCoords(track_rect.left(), - track_rect.bottom() - kScrollerTrackBorderWidth, - track_rect.right(), - track_rect.bottom(), - paint); - } -} - -void NativeThemeMac::PaintScrollbarThumb(SkCanvas* canvas, - Part part, - State state, - const gfx::Rect& rect) const { - gfx::Rect thumb_rect(rect); - switch (part) { - case kScrollbarHorizontalThumb: - thumb_rect.Inset(0, kScrollerTrackBorderWidth, 0, 0); - break; - case kScrollbarVerticalThumb: - thumb_rect.Inset(kScrollerTrackBorderWidth, 0, 0, 0); - break; - default: - NOTREACHED(); - break; - } - - thumb_rect.Inset(kScrollerThumbInset, kScrollerThumbInset); - - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(state == kHovered ? thumb_active_color_ - : thumb_inactive_color_); - const SkScalar radius = std::min(rect.width(), rect.height()); - canvas->drawRoundRect(gfx::RectToSkRect(thumb_rect), radius, radius, paint); -} - -void NativeThemeMac::PaintScrollbarCorner(SkCanvas* canvas, - State state, - const gfx::Rect& rect) const { - DCHECK_GT(rect.width(), 0); - DCHECK_GT(rect.height(), 0); - - // Draw radial gradient from top-left corner. - skia::RefPtr<SkShader> shader = skia::AdoptRef( - SkGradientShader::CreateRadial(SkPoint::Make(rect.x(), rect.y()), - rect.width(), - kScrollerTrackGradientColors, - NULL, - arraysize(kScrollerTrackGradientColors), - SkShader::kClamp_TileMode)); - SkPaint gradient; - gradient.setStyle(SkPaint::kFill_Style); - gradient.setAntiAlias(true); - gradient.setShader(shader.get()); - canvas->drawRect(gfx::RectToSkRect(rect), gradient); - - // Draw inner border corner point. - canvas->drawPoint(rect.x(), rect.y(), kScrollerTrackInnerBorderColor); - - // Draw outer borders. - SkPaint paint; - paint.setColor(kScrollerTrackOuterBorderColor); - canvas->drawRectCoords(rect.right() - kScrollerTrackBorderWidth, - rect.y(), - rect.right(), - rect.bottom(), - paint); - canvas->drawRectCoords(rect.x(), - rect.bottom() - kScrollerTrackBorderWidth, - rect.right(), - rect.bottom(), - paint); -} - void NativeThemeMac::PaintMenuPopupBackground( SkCanvas* canvas, const gfx::Size& size, const MenuBackgroundExtraParams& menu_background) const { SkPaint paint; paint.setAntiAlias(true); - if (base::mac::IsOSYosemiteOrLater()) - paint.setColor(kMenuPopupBackgroundColorYosemite); + if (base::mac::IsOSMavericks()) + paint.setColor(kMenuPopupBackgroundColorMavericks); else paint.setColor(kMenuPopupBackgroundColor); const SkScalar radius = SkIntToScalar(menu_background.corner_radius); @@ -384,7 +248,7 @@ void NativeThemeMac::PaintMenuItemBackground( SkCanvas* canvas, State state, const gfx::Rect& rect, - const MenuListExtraParams& menu_list) const { + const MenuItemExtraParams& menu_item) const { SkPaint paint; switch (state) { case NativeTheme::kNormal: @@ -405,11 +269,37 @@ void NativeThemeMac::PaintMenuItemBackground( } } +// static +sk_sp<SkShader> NativeThemeMac::GetButtonBackgroundShader( + NativeTheme::State state, int height) { + typedef SkColor ColorByState[NativeTheme::State::kNumStates]; + SkPoint gradient_points[2]; + gradient_points[0].iset(0, 0); + gradient_points[1].iset(0, height); + + SkScalar gradient_positions[] = { 0.0, 0.38, 1.0 }; + + ColorByState start_colors; + start_colors[NativeTheme::State::kDisabled] = gfx::kMaterialGrey300; + start_colors[NativeTheme::State::kHovered] = gfx::kMaterialBlue300; + start_colors[NativeTheme::State::kNormal] = gfx::kMaterialBlue300; + start_colors[NativeTheme::State::kPressed] = gfx::kMaterialBlue300; + ColorByState end_colors; + end_colors[NativeTheme::State::kDisabled] = gfx::kMaterialGrey300; + end_colors[NativeTheme::State::kHovered] = gfx::kMaterialBlue700; + end_colors[NativeTheme::State::kNormal] = gfx::kMaterialBlue700; + end_colors[NativeTheme::State::kPressed] = gfx::kMaterialBlue700; + + SkColor gradient_colors[] = { + start_colors[state], start_colors[state], end_colors[state] + }; + + return SkGradientShader::MakeLinear( + gradient_points, gradient_colors, gradient_positions, 3, + SkShader::kClamp_TileMode); +} + NativeThemeMac::NativeThemeMac() { - set_scrollbar_button_length(0); - SetScrollbarColors(kScrollerThumbColor, - kScrollerThumbHoverColor, - kScrollerTrackGradientColors[0]); } NativeThemeMac::~NativeThemeMac() { diff --git a/chromium/ui/native_theme/native_theme_win.cc b/chromium/ui/native_theme/native_theme_win.cc index 182a27b98b3..94f907500ef 100644 --- a/chromium/ui/native_theme/native_theme_win.cc +++ b/chromium/ui/native_theme/native_theme_win.cc @@ -24,7 +24,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColorPriv.h" #include "third_party/skia/include/core/SkShader.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/gdi_util.h" @@ -77,12 +77,9 @@ void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) { SkMatrix local_matrix; local_matrix.setTranslate(SkIntToScalar(align_rect.left), SkIntToScalar(align_rect.top)); - skia::RefPtr<SkShader> shader = - skia::AdoptRef(SkShader::CreateBitmapShader(bitmap, - SkShader::kRepeat_TileMode, - SkShader::kRepeat_TileMode, - &local_matrix)); - paint->setShader(shader.get()); + paint->setShader( + SkShader::MakeBitmapShader(bitmap, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, &local_matrix)); } // <-a-> @@ -196,10 +193,6 @@ NativeThemeWin* NativeThemeWin::instance() { gfx::Size NativeThemeWin::GetPartSize(Part part, State state, const ExtraParams& extra) const { - gfx::Size part_size = CommonThemeGetPartSize(part, state, extra); - if (!part_size.IsEmpty()) - return part_size; - // The GetThemePartSize call below returns the default size without // accounting for user customization (crbug/218291). switch (part) { @@ -245,9 +238,6 @@ void NativeThemeWin::Paint(SkCanvas* canvas, return; switch (part) { - case kComboboxArrow: - CommonThemePaintComboboxArrow(canvas, rect); - return; case kMenuPopupGutter: PaintMenuGutter(canvas, rect); return; @@ -258,7 +248,8 @@ void NativeThemeWin::Paint(SkCanvas* canvas, PaintMenuBackground(canvas, rect); return; case kMenuItemBackground: - CommonThemePaintMenuItemBackground(this, canvas, state, rect); + CommonThemePaintMenuItemBackground(this, canvas, state, rect, + extra.menu_item); return; default: break; @@ -466,7 +457,6 @@ void NativeThemeWin::PaintDirect(SkCanvas* canvas, case kWindowResizeGripper: PaintWindowResizeGripper(hdc, rect); return; - case kComboboxArrow: case kSliderTrack: case kSliderThumb: case kMaxPart: @@ -722,12 +712,7 @@ void NativeThemeWin::PaintIndirect(SkCanvas* canvas, // Draw the theme controls using existing HDC-drawing code. PaintDirect(&offscreen_canvas, part, state, adjusted_rect, adjusted_extra); - // Copy the pixels to a bitmap that has ref-counted pixel storage, which is - // necessary to have when drawing to a SkPicture. - const SkBitmap& hdc_bitmap = - offscreen_canvas.getDevice()->accessBitmap(false); - SkBitmap bitmap; - hdc_bitmap.copyTo(&bitmap, kN32_SkColorType); + SkBitmap bitmap = skia::ReadPixels(&offscreen_canvas); // Post-process the pixels to fix up the alpha values (see big comment above). const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder); @@ -1715,7 +1700,6 @@ NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) { return TEXTFIELD; case kWindowResizeGripper: return STATUS; - case kComboboxArrow: case kMenuCheckBackground: case kMenuPopupBackground: case kMenuItemBackground: @@ -1761,7 +1745,6 @@ int NativeThemeWin::GetWindowsPart(Part part, return SBP_THUMBBTNVERT; case kWindowResizeGripper: return SP_GRIPPER; - case kComboboxArrow: case kInnerSpinButton: case kMenuList: case kMenuCheckBackground: @@ -1955,7 +1938,6 @@ int NativeThemeWin::GetWindowsState(Part part, NOTREACHED(); return 0; } - case kComboboxArrow: case kInnerSpinButton: case kMenuList: case kMenuCheckBackground: diff --git a/chromium/ui/ozone/BUILD.gn b/chromium/ui/ozone/BUILD.gn index e6881de0bef..1470e380867 100644 --- a/chromium/ui/ozone/BUILD.gn +++ b/chromium/ui/ozone/BUILD.gn @@ -8,6 +8,8 @@ import("//testing/test.gni") assert(use_ozone) +visibility = [ ":*" ] + # The list of platforms that will be built. ozone_platforms = [] @@ -43,6 +45,17 @@ if (ozone_platform_cast) { ozone_platform_deps += [ "platform/cast" ] } +if (ozone_platform_wayland) { + ozone_platforms += [ "wayland" ] + ozone_platform_deps += [ "platform/wayland" ] + ozone_platform_test_deps += [ "platform/wayland:wayland_unittests" ] +} + +if (ozone_platform_x11) { + ozone_platforms += [ "x11" ] + ozone_platform_deps += [ "platform/x11" ] +} + platform_list_cc_file = "$target_gen_dir/platform_list.cc" platform_list_h_file = "$target_gen_dir/platform_list.h" platform_list_txt_file = "$target_gen_dir/platform_list.txt" @@ -57,23 +70,6 @@ config("vgem_map") { # GYP version: ui/ozone/ozone.gyp:ozone_base component("ozone_base") { sources = [ - "common/display_mode_proxy.cc", - "common/display_mode_proxy.h", - "common/display_snapshot_proxy.cc", - "common/display_snapshot_proxy.h", - "common/display_util.cc", - "common/display_util.h", - "common/egl_util.cc", - "common/egl_util.h", - "common/gpu/ozone_gpu_message_generator.cc", - "common/gpu/ozone_gpu_message_generator.h", - "common/gpu/ozone_gpu_message_params.cc", - "common/gpu/ozone_gpu_message_params.h", - "common/gpu/ozone_gpu_messages.h", - "common/native_display_delegate_ozone.cc", - "common/native_display_delegate_ozone.h", - "common/stub_overlay_manager.cc", - "common/stub_overlay_manager.h", "public/cursor_factory_ozone.cc", "public/cursor_factory_ozone.h", "public/gpu_platform_support.cc", @@ -97,7 +93,7 @@ component("ozone_base") { defines = [ "OZONE_BASE_IMPLEMENTATION" ] - deps = [ + public_deps = [ "//base", "//ipc", "//skia", @@ -108,51 +104,76 @@ component("ozone_base") { "//ui/gfx", "//ui/gfx/geometry", "//ui/gfx/ipc", + "//ui/gfx/ipc/skia", + ] + + visibility += [ + # Everyone should depend on //ui/ozone instead except a handful of + # things that would otherwise create a cycle. + "//ui/base", + "//ui/events/ozone/*", + "//ui/ozone/platform/*", + "//ui/ozone/common/*", ] } -component("ozone") { +source_set("platform") { sources = [ "common/stub_client_native_pixmap_factory.cc", "common/stub_client_native_pixmap_factory.h", "platform_selection.cc", "platform_selection.h", "public/client_native_pixmap_factory.cc", - "public/client_native_pixmap_factory.h", "public/ozone_gpu_test_helper.cc", - "public/ozone_gpu_test_helper.h", "public/ozone_platform.cc", - "public/ozone_platform.h", constructor_list_cc_file, platform_list_cc_file, platform_list_h_file, ] + public = [ + "public/client_native_pixmap_factory.h", + "public/ozone_gpu_test_helper.h", + "public/ozone_platform.h", + ] + defines = [ "OZONE_IMPLEMENTATION" ] public_deps = [ ":ozone_base", + "//base", + "//ipc", + "//skia", + "//ui/display/types", + "//ui/events", + "//ui/events/devices", + "//ui/gfx", + "//ui/gfx/geometry", + "//ui/platform_window", ] - deps = - [ - ":generate_constructor_list", - ":generate_ozone_platform_list", - "//base", - "//ipc", - "//skia", - "//ui/display/types", - "//ui/display/util", - "//ui/events", - "//ui/events/devices", - "//ui/events/ozone:events_ozone", - "//ui/gfx", - "//ui/gfx/geometry", - "//ui/gfx/ipc", - - # TODO(GYP) the GYP version has a way to add additional dependencies via - # build flags. - ] + ozone_platform_deps + deps = [ + ":generate_constructor_list", + ":generate_ozone_platform_list", + ] + + # TODO(GYP) the GYP version has a way to add additional dependencies via + # build flags. + deps += ozone_platform_deps + + # Platforms are always linked into //ui/ozone and can include our headers. + allow_circular_includes_from = ozone_platform_deps + + # This is used for platform tests. + visibility += [ "//ui/ozone/platform/*" ] +} + +component("ozone") { + visibility = [] + visibility = [ "*" ] + public_deps = [ + ":platform", + ] } # GYP version: ui/ozone/ozone.gyp:generate_ozone_platform_list @@ -171,6 +192,8 @@ action("generate_ozone_platform_list") { "--output_txt=" + rebase_path(platform_list_txt_file, root_build_dir), "--default=$ozone_platform", ] + ozone_platforms + + visibility += [ "//media:*" ] } # GYP version: ui/ozone/ozone.gyp:generate_constructor_list @@ -205,8 +228,15 @@ test("ozone_unittests") { ] deps = [ - "//base/test:test_support", - "//testing/gtest", - "//ui/gfx/geometry", - ] + ozone_platform_test_deps + "//base/test:test_support", + "//testing/gtest", + "//ui/gfx/geometry", + ] + + # Add tests of platform internals. + deps += ozone_platform_test_deps + + # Platform tests link ozone statically. Make sure we're not getting a + # 2nd copy of any code via the component. + assert_no_deps = [ "//ui/ozone" ] } diff --git a/chromium/ui/ozone/common/BUILD.gn b/chromium/ui/ozone/common/BUILD.gn new file mode 100644 index 00000000000..0f88278ef64 --- /dev/null +++ b/chromium/ui/ozone/common/BUILD.gn @@ -0,0 +1,35 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ui.gni") + +assert(use_ozone) + +source_set("common") { + sources = [ + "display_mode_proxy.cc", + "display_mode_proxy.h", + "display_snapshot_proxy.cc", + "display_snapshot_proxy.h", + "display_util.cc", + "display_util.h", + "egl_util.cc", + "egl_util.h", + "gpu/ozone_gpu_message_generator.cc", + "gpu/ozone_gpu_message_generator.h", + "gpu/ozone_gpu_message_params.cc", + "gpu/ozone_gpu_message_params.h", + "gpu/ozone_gpu_messages.h", + "native_display_delegate_ozone.cc", + "native_display_delegate_ozone.h", + "stub_overlay_manager.cc", + "stub_overlay_manager.h", + ] + + public_deps = [ + "//ui/ozone:ozone_base", + ] + + visibility = [ "//ui/ozone/platform/*" ] +} diff --git a/chromium/ui/ozone/common/display_mode_proxy.h b/chromium/ui/ozone/common/display_mode_proxy.h index 0b353791f6c..c1821acb742 100644 --- a/chromium/ui/ozone/common/display_mode_proxy.h +++ b/chromium/ui/ozone/common/display_mode_proxy.h @@ -7,13 +7,12 @@ #include "base/macros.h" #include "ui/display/types/display_mode.h" -#include "ui/ozone/ozone_base_export.h" namespace ui { struct DisplayMode_Params; -class OZONE_BASE_EXPORT DisplayModeProxy : public DisplayMode { +class DisplayModeProxy : public DisplayMode { public: DisplayModeProxy(const DisplayMode_Params& params); ~DisplayModeProxy() override; diff --git a/chromium/ui/ozone/common/display_snapshot_proxy.cc b/chromium/ui/ozone/common/display_snapshot_proxy.cc index 040f82d3270..e057599c781 100644 --- a/chromium/ui/ozone/common/display_snapshot_proxy.cc +++ b/chromium/ui/ozone/common/display_snapshot_proxy.cc @@ -18,13 +18,6 @@ bool SameModes(const DisplayMode_Params& lhs, const DisplayMode_Params& rhs) { lhs.refresh_rate == rhs.refresh_rate; } -// Exclude 4K@60kHz becaseu this doesn't work in most devices/configuration now. -// TODO(marcheu|oshima): Revisit this. crbug.com/39397 -bool IsModeBlackListed(const DisplayMode_Params& mode_params) { - return mode_params.size.width() >= 3840 && - mode_params.size.height() >= 2160 && mode_params.refresh_rate >= 60.0f; -} - } // namespace DisplaySnapshotProxy::DisplaySnapshotProxy(const DisplaySnapshot_Params& params) @@ -34,17 +27,16 @@ DisplaySnapshotProxy::DisplaySnapshotProxy(const DisplaySnapshot_Params& params) params.type, params.is_aspect_preserving_scaling, params.has_overscan, + params.has_color_correction_matrix, params.display_name, params.sys_path, std::vector<const DisplayMode*>(), + params.edid, NULL, NULL), string_representation_(params.string_representation) { for (size_t i = 0; i < params.modes.size(); ++i) { - const DisplayMode_Params& mode_params = params.modes[i]; - if (IsModeBlackListed(mode_params)) - continue; - modes_.push_back(new DisplayModeProxy(mode_params)); + modes_.push_back(new DisplayModeProxy(params.modes[i])); if (params.has_current_mode && SameModes(params.modes[i], params.current_mode)) diff --git a/chromium/ui/ozone/common/display_snapshot_proxy.h b/chromium/ui/ozone/common/display_snapshot_proxy.h index f2a53eb7596..6a5c4c96028 100644 --- a/chromium/ui/ozone/common/display_snapshot_proxy.h +++ b/chromium/ui/ozone/common/display_snapshot_proxy.h @@ -7,13 +7,12 @@ #include "base/macros.h" #include "ui/display/types/display_snapshot.h" -#include "ui/ozone/ozone_base_export.h" namespace ui { struct DisplaySnapshot_Params; -class OZONE_BASE_EXPORT DisplaySnapshotProxy : public DisplaySnapshot { +class DisplaySnapshotProxy : public DisplaySnapshot { public: DisplaySnapshotProxy(const DisplaySnapshot_Params& params); ~DisplaySnapshotProxy() override; diff --git a/chromium/ui/ozone/common/display_util.cc b/chromium/ui/ozone/common/display_util.cc index 742dbcf9e3c..254f320b1b4 100644 --- a/chromium/ui/ozone/common/display_util.cc +++ b/chromium/ui/ozone/common/display_util.cc @@ -49,11 +49,14 @@ DisplaySnapshot_Params GetDisplaySnapshotParams( params.type = display.type(); params.is_aspect_preserving_scaling = display.is_aspect_preserving_scaling(); params.has_overscan = display.has_overscan(); + params.has_color_correction_matrix = display.has_color_correction_matrix(); params.display_name = display.display_name(); params.sys_path = display.sys_path(); for (size_t i = 0; i < display.modes().size(); ++i) params.modes.push_back(GetDisplayModeParams(*display.modes()[i])); + params.edid = display.edid(); + params.has_current_mode = display.current_mode() != NULL; if (params.has_current_mode) params.current_mode = GetDisplayModeParams(*display.current_mode()); diff --git a/chromium/ui/ozone/common/display_util.h b/chromium/ui/ozone/common/display_util.h index efa4e912961..988712631da 100644 --- a/chromium/ui/ozone/common/display_util.h +++ b/chromium/ui/ozone/common/display_util.h @@ -10,7 +10,6 @@ #include <vector> #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" -#include "ui/ozone/ozone_base_export.h" namespace base { class FilePath; @@ -33,8 +32,7 @@ class FindDisplayById { int64_t display_id_; }; -DisplayMode_Params OZONE_BASE_EXPORT -GetDisplayModeParams(const DisplayMode& mode); +DisplayMode_Params GetDisplayModeParams(const DisplayMode& mode); DisplaySnapshot_Params GetDisplaySnapshotParams(const DisplaySnapshot& display); // Create a display using the Ozone command line parameters. diff --git a/chromium/ui/ozone/common/egl_util.cc b/chromium/ui/ozone/common/egl_util.cc index 8000360c7b0..a86704640ad 100644 --- a/chromium/ui/ozone/common/egl_util.cc +++ b/chromium/ui/ozone/common/egl_util.cc @@ -62,4 +62,27 @@ bool LoadEGLGLES2Bindings( return true; } +void* /* EGLConfig */ ChooseEGLConfig(const EglConfigCallbacks& egl, + const int32_t* attributes) { + void* config; + int32_t num_configs; + if (!egl.choose_config.Run(attributes, nullptr, 0, &num_configs)) { + LOG(ERROR) << "eglChooseConfig failed with error " + << egl.get_last_error_string.Run(); + return nullptr; + } + + if (num_configs == 0) { + LOG(ERROR) << "No suitable EGL configs found."; + return nullptr; + } + + if (!egl.choose_config.Run(attributes, &config, 1, &num_configs)) { + LOG(ERROR) << "eglChooseConfig failed with error " + << egl.get_last_error_string.Run(); + return nullptr; + } + return config; +} + } // namespace ui diff --git a/chromium/ui/ozone/common/egl_util.h b/chromium/ui/ozone/common/egl_util.h index 8e20387fc25..dbf9a8f86e0 100644 --- a/chromium/ui/ozone/common/egl_util.h +++ b/chromium/ui/ozone/common/egl_util.h @@ -5,18 +5,16 @@ #ifndef UI_OZONE_COMMON_EGL_UTIL_H_ #define UI_OZONE_COMMON_EGL_UTIL_H_ -#include "ui/ozone/ozone_base_export.h" #include "ui/ozone/public/surface_factory_ozone.h" +#include "ui/ozone/public/surface_ozone_egl.h" namespace ui { -OZONE_BASE_EXPORT bool LoadDefaultEGLGLES2Bindings( SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library, SurfaceFactoryOzone::SetGLGetProcAddressProcCallback set_gl_get_proc_address); -OZONE_BASE_EXPORT bool LoadEGLGLES2Bindings( SurfaceFactoryOzone::AddGLLibraryCallback add_gl_library, SurfaceFactoryOzone::SetGLGetProcAddressProcCallback @@ -24,6 +22,9 @@ bool LoadEGLGLES2Bindings( const char* egl_library_name, const char* gles_library_name); +void* /* EGLConfig */ ChooseEGLConfig(const EglConfigCallbacks& egl, + const int32_t* attributes); + } // namespace ui #endif // UI_OZONE_COMMON_EGL_UTIL_H_ 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 9b0c9a08c38..7924ee040b0 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.cc +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.cc @@ -6,6 +6,7 @@ #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/ipc/gfx_param_traits.h" +#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h" namespace ui { @@ -17,6 +18,9 @@ DisplayMode_Params::~DisplayMode_Params() {} DisplaySnapshot_Params::DisplaySnapshot_Params() { } +DisplaySnapshot_Params::DisplaySnapshot_Params( + const DisplaySnapshot_Params& other) = default; + DisplaySnapshot_Params::~DisplaySnapshot_Params() {} OverlayCheck_Params::OverlayCheck_Params() {} @@ -30,6 +34,9 @@ OverlayCheck_Params::OverlayCheck_Params( crop_rect(candidate.crop_rect), plane_z_order(candidate.plane_z_order) {} +OverlayCheck_Params::OverlayCheck_Params(const OverlayCheck_Params& other) = + default; + OverlayCheck_Params::~OverlayCheck_Params() { } 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 f38be3c335c..afb0b071d8f 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h @@ -16,12 +16,11 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/overlay_transform.h" -#include "ui/ozone/ozone_base_export.h" #include "ui/ozone/public/overlay_candidates_ozone.h" namespace ui { -struct OZONE_BASE_EXPORT DisplayMode_Params { +struct DisplayMode_Params { DisplayMode_Params(); ~DisplayMode_Params(); @@ -30,8 +29,9 @@ struct OZONE_BASE_EXPORT DisplayMode_Params { float refresh_rate = 0.0f; }; -struct OZONE_BASE_EXPORT DisplaySnapshot_Params { +struct DisplaySnapshot_Params { DisplaySnapshot_Params(); + DisplaySnapshot_Params(const DisplaySnapshot_Params& other); ~DisplaySnapshot_Params(); int64_t display_id = 0; @@ -40,9 +40,11 @@ struct OZONE_BASE_EXPORT DisplaySnapshot_Params { DisplayConnectionType type = DISPLAY_CONNECTION_TYPE_NONE; bool is_aspect_preserving_scaling = false; bool has_overscan = false; + bool has_color_correction_matrix = false; std::string display_name; base::FilePath sys_path; std::vector<DisplayMode_Params> modes; + std::vector<uint8_t> edid; bool has_current_mode = false; DisplayMode_Params current_mode; bool has_native_mode = false; @@ -51,10 +53,11 @@ struct OZONE_BASE_EXPORT DisplaySnapshot_Params { std::string string_representation; }; -struct OZONE_BASE_EXPORT OverlayCheck_Params { +struct OverlayCheck_Params { OverlayCheck_Params(); OverlayCheck_Params( const OverlayCandidatesOzone::OverlaySurfaceCandidate& candidate); + OverlayCheck_Params(const OverlayCheck_Params& other); ~OverlayCheck_Params(); bool operator<(const OverlayCheck_Params& plane) const; diff --git a/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h b/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h index 95f84bfadd6..fab2f1887f8 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h @@ -18,12 +18,12 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/ipc/gfx_param_traits.h" #include "ui/gfx/ipc/gfx_param_traits_macros.h" +#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" -#include "ui/ozone/ozone_base_export.h" #undef IPC_MESSAGE_EXPORT -#define IPC_MESSAGE_EXPORT OZONE_BASE_EXPORT +#define IPC_MESSAGE_EXPORT #define IPC_MESSAGE_START OzoneGpuMsgStart @@ -48,9 +48,11 @@ IPC_STRUCT_TRAITS_BEGIN(ui::DisplaySnapshot_Params) IPC_STRUCT_TRAITS_MEMBER(type) IPC_STRUCT_TRAITS_MEMBER(is_aspect_preserving_scaling) IPC_STRUCT_TRAITS_MEMBER(has_overscan) + IPC_STRUCT_TRAITS_MEMBER(has_color_correction_matrix) IPC_STRUCT_TRAITS_MEMBER(display_name) IPC_STRUCT_TRAITS_MEMBER(sys_path) IPC_STRUCT_TRAITS_MEMBER(modes) + IPC_STRUCT_TRAITS_MEMBER(edid) IPC_STRUCT_TRAITS_MEMBER(has_current_mode) IPC_STRUCT_TRAITS_MEMBER(current_mode) IPC_STRUCT_TRAITS_MEMBER(has_native_mode) @@ -140,10 +142,11 @@ IPC_MESSAGE_CONTROL2(OzoneGpuMsg_SetHDCPState, int64_t /* display_id */, ui::HDCPState /* state */) -// Provides the gamma ramp for display adjustment. -IPC_MESSAGE_CONTROL2(OzoneGpuMsg_SetGammaRamp, +IPC_MESSAGE_CONTROL4(OzoneGpuMsg_SetColorCorrection, int64_t, // display ID, - std::vector<ui::GammaRampRGBEntry>) // lut + std::vector<ui::GammaRampRGBEntry>, // degamma lut + std::vector<ui::GammaRampRGBEntry>, // gamma lut + std::vector<float>) // transform matrix IPC_MESSAGE_CONTROL2(OzoneGpuMsg_CheckOverlayCapabilities, gfx::AcceleratedWidget /* widget */, diff --git a/chromium/ui/ozone/common/native_display_delegate_ozone.cc b/chromium/ui/ozone/common/native_display_delegate_ozone.cc index 7dd50377714..24703cd3dd3 100644 --- a/chromium/ui/ozone/common/native_display_delegate_ozone.cc +++ b/chromium/ui/ozone/common/native_display_delegate_ozone.cc @@ -110,9 +110,11 @@ bool NativeDisplayDelegateOzone::SetColorCalibrationProfile( return false; } -bool NativeDisplayDelegateOzone::SetGammaRamp( +bool NativeDisplayDelegateOzone::SetColorCorrection( const ui::DisplaySnapshot& output, - const std::vector<GammaRampRGBEntry>& lut) { + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { NOTIMPLEMENTED(); return false; } diff --git a/chromium/ui/ozone/common/native_display_delegate_ozone.h b/chromium/ui/ozone/common/native_display_delegate_ozone.h index 8c5a8cbec95..baf2a6cf114 100644 --- a/chromium/ui/ozone/common/native_display_delegate_ozone.h +++ b/chromium/ui/ozone/common/native_display_delegate_ozone.h @@ -8,14 +8,13 @@ #include <stdint.h> #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "ui/display/types/native_display_delegate.h" -#include "ui/ozone/ozone_base_export.h" namespace ui { -class OZONE_BASE_EXPORT NativeDisplayDelegateOzone - : public NativeDisplayDelegate { +class NativeDisplayDelegateOzone : public NativeDisplayDelegate { public: NativeDisplayDelegateOzone(); ~NativeDisplayDelegateOzone() override; @@ -48,9 +47,10 @@ class OZONE_BASE_EXPORT NativeDisplayDelegateOzone bool SetColorCalibrationProfile( const ui::DisplaySnapshot& output, ui::ColorCalibrationProfile new_profile) override; - bool SetGammaRamp(const ui::DisplaySnapshot& output, - const std::vector<GammaRampRGBEntry>& lut) override; - + bool SetColorCorrection(const ui::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; diff --git a/chromium/ui/ozone/common/stub_client_native_pixmap_factory.cc b/chromium/ui/ozone/common/stub_client_native_pixmap_factory.cc index 540e1ec8adb..ad32d8a67b7 100644 --- a/chromium/ui/ozone/common/stub_client_native_pixmap_factory.cc +++ b/chromium/ui/ozone/common/stub_client_native_pixmap_factory.cc @@ -15,7 +15,6 @@ class StubClientNativePixmapFactory : public ClientNativePixmapFactory { ~StubClientNativePixmapFactory() override {} // ClientNativePixmapFactory: - void Initialize(base::ScopedFD device_fd) override {} bool IsConfigurationSupported(gfx::BufferFormat format, gfx::BufferUsage usage) const override { return false; diff --git a/chromium/ui/ozone/common/stub_client_native_pixmap_factory.h b/chromium/ui/ozone/common/stub_client_native_pixmap_factory.h index 27192f78992..a38a5571952 100644 --- a/chromium/ui/ozone/common/stub_client_native_pixmap_factory.h +++ b/chromium/ui/ozone/common/stub_client_native_pixmap_factory.h @@ -5,7 +5,7 @@ #ifndef UI_OZONE_COMMON_STUB_CLIENT_NATIVE_PIXMAP_FACTORY_H_ #define UI_OZONE_COMMON_STUB_CLIENT_NATIVE_PIXMAP_FACTORY_H_ -#include "ui/ozone/public/client_native_pixmap_factory.h" // nogncheck +#include "ui/ozone/public/client_native_pixmap_factory.h" namespace ui { diff --git a/chromium/ui/ozone/common/stub_overlay_manager.h b/chromium/ui/ozone/common/stub_overlay_manager.h index 3d42c2b6652..54fa3a36825 100644 --- a/chromium/ui/ozone/common/stub_overlay_manager.h +++ b/chromium/ui/ozone/common/stub_overlay_manager.h @@ -6,12 +6,11 @@ #define UI_OZONE_COMMON_STUB_OVERLAY_MANAGER_H_ #include "base/macros.h" -#include "ui/ozone/ozone_base_export.h" #include "ui/ozone/public/overlay_manager_ozone.h" namespace ui { -class OZONE_BASE_EXPORT StubOverlayManager : public OverlayManagerOzone { +class StubOverlayManager : public OverlayManagerOzone { public: StubOverlayManager(); ~StubOverlayManager() override; diff --git a/chromium/ui/ozone/demo/BUILD.gn b/chromium/ui/ozone/demo/BUILD.gn index 70b2ded5a27..c05bb92ee2e 100644 --- a/chromium/ui/ozone/demo/BUILD.gn +++ b/chromium/ui/ozone/demo/BUILD.gn @@ -33,7 +33,6 @@ executable("ozone_demo") { "//ui/gfx/geometry", "//ui/gl", "//ui/ozone", - "//ui/ozone:ozone_base", "//ui/platform_window", ] } diff --git a/chromium/ui/ozone/demo/ozone_demo.cc b/chromium/ui/ozone/demo/ozone_demo.cc index 1e386ebc1bd..151c2803e61 100644 --- a/chromium/ui/ozone/demo/ozone_demo.cc +++ b/chromium/ui/ozone/demo/ozone_demo.cc @@ -145,8 +145,7 @@ class DemoWindow : public ui::PlatformWindowDelegate { void OnBoundsChanged(const gfx::Rect& new_bounds) override {} void OnDamageRect(const gfx::Rect& damaged_region) override {} void DispatchEvent(ui::Event* event) override { - if (event->IsKeyEvent() && - static_cast<ui::KeyEvent*>(event)->code() == ui::DomCode::US_Q) + if (event->IsKeyEvent() && event->AsKeyEvent()->code() == ui::DomCode::US_Q) Quit(); } void OnCloseRequest() override { Quit(); } diff --git a/chromium/ui/ozone/demo/software_renderer.cc b/chromium/ui/ozone/demo/software_renderer.cc index fb567809f10..175e37998c3 100644 --- a/chromium/ui/ozone/demo/software_renderer.cc +++ b/chromium/ui/ozone/demo/software_renderer.cc @@ -49,7 +49,7 @@ void SoftwareRenderer::RenderFrame() { float fraction = NextFraction(); - skia::RefPtr<SkSurface> surface = software_surface_->GetSurface(); + sk_sp<SkSurface> surface = software_surface_->GetSurface(); SkColor color = SkColorSetARGB(0xff, 0, 0xff * fraction, 0xff * (1 - fraction)); diff --git a/chromium/ui/ozone/demo/surfaceless_gl_renderer.h b/chromium/ui/ozone/demo/surfaceless_gl_renderer.h index c36c22fb7fd..6c8524e6fd6 100644 --- a/chromium/ui/ozone/demo/surfaceless_gl_renderer.h +++ b/chromium/ui/ozone/demo/surfaceless_gl_renderer.h @@ -6,6 +6,7 @@ #define UI_OZONE_DEMO_SURFACELESS_GL_RENDERER_H_ #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "ui/ozone/demo/gl_renderer.h" namespace gl { diff --git a/chromium/ui/ozone/empty.cc b/chromium/ui/ozone/empty.cc new file mode 100644 index 00000000000..154ead7673b --- /dev/null +++ b/chromium/ui/ozone/empty.cc @@ -0,0 +1,6 @@ +// 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. + +// This empty source file exists to force GYP to link the +// ui/ozone/ozone.gyp:ozone target using the C++ compiler. diff --git a/chromium/ui/ozone/ozone.gni b/chromium/ui/ozone/ozone.gni index 8d12805c205..768ddf74a93 100644 --- a/chromium/ui/ozone/ozone.gni +++ b/chromium/ui/ozone/ozone.gni @@ -3,10 +3,11 @@ # found in the LICENSE file. import("//build/config/chromecast_build.gni") +import("//build/config/ui.gni") declare_args() { # Select platforms automatically. Turn this off for manual control. - ozone_auto_platforms = true + ozone_auto_platforms = use_ozone # This enables memory-mapped access to accelerated graphics buffers via the # VGEM ("virtual GEM") driver. This is currently only available on Chrome OS @@ -28,6 +29,7 @@ declare_args() { ozone_platform_ozonex = false ozone_platform_headless = false ozone_platform_x11 = false + ozone_platform_wayland = false if (ozone_auto_platforms) { # Use headless as the default platform. @@ -35,30 +37,30 @@ declare_args() { ozone_platform_headless = true if (is_chromecast) { - if (!disable_display) { - # Enable the Cast ozone platform on all A/V Cast builds. - ozone_platform_cast = true + # Enable the Cast ozone platform on all A/V Cast builds. + ozone_platform_cast = true - # For desktop Chromecast builds, override the default "headless" - # platform with --ozone-platform=egltest - # TODO(halliwell): Create a libcast_graphics implementation for desktop - # using X11, and disable these two platforms. "cast" platform should be - # the default on every A/V build. - if (is_cast_desktop_build) { - ozone_platform_egltest = true - ozone_platform_ozonex = true - } else { - # On device builds, enable "cast" as the default platform. - ozone_platform = "cast" - } + # For visual desktop Chromecast builds, override the default "headless" + # platform with --ozone-platform=x11. + # TODO(halliwell): Create a libcast_graphics implementation for desktop + # using X11, and disable this platform. + if (is_cast_desktop_build && !disable_display) { + ozone_platform_x11 = true + } else { + ozone_platform = "cast" } } else if (is_chromeos) { ozone_platform_gbm = true - ozone_platform_egltest = true + ozone_platform_x11 = true } else { # Build all platforms whose deps are in install-build-deps.sh. # Only these platforms will be compile tested by buildbots. - ozone_platform_egltest = true + ozone_platform_x11 = true } } } + +assert(use_ozone || !(ozone_platform_caca || ozone_platform_cast || + ozone_platform_egltest || ozone_platform_gbm || + ozone_platform_ozonex || ozone_platform_headless), + "Must set use_ozone to select ozone platforms") diff --git a/chromium/ui/ozone/ozone.gyp b/chromium/ui/ozone/ozone.gyp index a878f8ec33f..0458cf3406b 100644 --- a/chromium/ui/ozone/ozone.gyp +++ b/chromium/ui/ozone/ozone.gyp @@ -37,28 +37,12 @@ '<(DEPTH)/ui/display/display.gyp:display_util', '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry', '<(DEPTH)/ui/gfx/ipc/gfx_ipc.gyp:gfx_ipc', + '<(DEPTH)/ui/gfx/ipc/skia/gfx_ipc_skia.gyp:gfx_ipc_skia', ], 'defines': [ 'OZONE_BASE_IMPLEMENTATION', ], 'sources': [ - 'common/display_mode_proxy.cc', - 'common/display_mode_proxy.h', - 'common/display_snapshot_proxy.cc', - 'common/display_snapshot_proxy.h', - 'common/display_util.cc', - 'common/display_util.h', - 'common/egl_util.cc', - 'common/egl_util.h', - 'common/gpu/ozone_gpu_message_generator.cc', - 'common/gpu/ozone_gpu_message_generator.h', - 'common/gpu/ozone_gpu_message_params.cc', - 'common/gpu/ozone_gpu_message_params.h', - 'common/gpu/ozone_gpu_messages.h', - 'common/native_display_delegate_ozone.cc', - 'common/native_display_delegate_ozone.h', - 'common/stub_overlay_manager.cc', - 'common/stub_overlay_manager.h', 'public/client_native_pixmap.h', 'public/cursor_factory_ozone.cc', 'public/cursor_factory_ozone.h', @@ -83,9 +67,43 @@ ], }, { - # GN version: //ui/ozone - 'target_name': 'ozone', - 'type': '<(component)', + # GN version: //ui/ozone/common + 'target_name': 'ozone_common', + 'type': 'static_library', + 'dependencies': [ + 'ozone_base', + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/ipc/ipc.gyp:ipc', + '<(DEPTH)/skia/skia.gyp:skia', + '<(DEPTH)/ui/display/display.gyp:display_types', + '<(DEPTH)/ui/display/display.gyp:display_util', + '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry', + '<(DEPTH)/ui/gfx/ipc/skia/gfx_ipc_skia.gyp:gfx_ipc_skia', + ], + 'sources': [ + 'common/display_mode_proxy.cc', + 'common/display_mode_proxy.h', + 'common/display_snapshot_proxy.cc', + 'common/display_snapshot_proxy.h', + 'common/display_util.cc', + 'common/display_util.h', + 'common/egl_util.cc', + 'common/egl_util.h', + 'common/gpu/ozone_gpu_message_generator.cc', + 'common/gpu/ozone_gpu_message_generator.h', + 'common/gpu/ozone_gpu_message_params.cc', + 'common/gpu/ozone_gpu_message_params.h', + 'common/gpu/ozone_gpu_messages.h', + 'common/native_display_delegate_ozone.cc', + 'common/native_display_delegate_ozone.h', + 'common/stub_overlay_manager.cc', + 'common/stub_overlay_manager.h', + ], + }, + { + # GN version: //ui/ozone:platform + 'target_name': 'ozone_platform', + 'type': 'static_library', 'dependencies': [ '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/ipc/ipc.gyp:ipc', @@ -97,7 +115,7 @@ '<(DEPTH)/ui/events/ozone/events_ozone.gyp:events_ozone', '<(DEPTH)/ui/gfx/gfx.gyp:gfx', '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry', - '<(DEPTH)/ui/gfx/ipc/gfx_ipc.gyp:gfx_ipc', + '<(DEPTH)/ui/gfx/ipc/skia/gfx_ipc_skia.gyp:gfx_ipc_skia', '<@(external_ozone_platform_deps)', '<@(internal_ozone_platform_deps)', 'ozone_base', @@ -195,13 +213,23 @@ ], }, { + # GN version: //ui/ozone + 'target_name': 'ozone', + 'type': '<(component)', + 'sources': [ + 'empty.cc', + ], + 'dependencies': [ + 'ozone_platform', + ], + }, + { 'target_name': 'ozone_unittests', 'type': '<(gtest_target_type)', 'sources': [ 'run_all_unittests.cc', ], 'dependencies': [ - 'ozone', '../../base/base.gyp:base', '../../base/base.gyp:test_support_base', '../../testing/gtest.gyp:gtest', @@ -250,5 +278,10 @@ 'platform/headless/headless.gypi', ], }], + ['<(ozone_platform_wayland) == 1', { + 'includes': [ + 'platform/wayland/wayland.gypi', + ], + }], ], } diff --git a/chromium/ui/ozone/platform/caca/BUILD.gn b/chromium/ui/ozone/platform/caca/BUILD.gn index 4f92fce9a08..be3fbb9d435 100644 --- a/chromium/ui/ozone/platform/caca/BUILD.gn +++ b/chromium/ui/ozone/platform/caca/BUILD.gn @@ -4,6 +4,8 @@ import("//build/config/linux/pkg_config.gni") +visibility = [ "//ui/ozone/*" ] + source_set("caca") { sources = [ "caca_event_source.cc", @@ -29,6 +31,8 @@ source_set("caca") { "//ui/gfx", "//ui/gfx/geometry", "//ui/ozone:ozone_base", + "//ui/ozone/common", + "//ui/platform_window", ] configs += [ ":libcaca" ] diff --git a/chromium/ui/ozone/platform/caca/caca.gypi b/chromium/ui/ozone/platform/caca/caca.gypi index fc1b6d20651..4fccd3d374e 100644 --- a/chromium/ui/ozone/platform/caca/caca.gypi +++ b/chromium/ui/ozone/platform/caca/caca.gypi @@ -19,6 +19,8 @@ 'OZONE_IMPLEMENTATION', ], 'dependencies': [ + 'ozone.gyp:ozone_base', + 'ozone.gyp:ozone_common', '../../base/base.gyp:base', '../../skia/skia.gyp:skia', '../events/events.gyp:events', diff --git a/chromium/ui/ozone/platform/caca/caca_window_manager.cc b/chromium/ui/ozone/platform/caca/caca_window_manager.cc index 11fc46d6635..561a57d8864 100644 --- a/chromium/ui/ozone/platform/caca/caca_window_manager.cc +++ b/chromium/ui/ozone/platform/caca/caca_window_manager.cc @@ -140,7 +140,7 @@ scoped_ptr<ui::SurfaceOzoneCanvas> CacaWindowManager::CreateCanvasForWidget( scoped_ptr<CacaSurface> canvas(new CacaSurface(window)); bool initialized = canvas->Initialize(); DCHECK(initialized); - return canvas; + return std::move(canvas); } } // namespace ui diff --git a/chromium/ui/ozone/platform/caca/ozone_platform_caca.cc b/chromium/ui/ozone/platform/caca/ozone_platform_caca.cc index fa2d2142225..dff1fdd2fb4 100644 --- a/chromium/ui/ozone/platform/caca/ozone_platform_caca.cc +++ b/chromium/ui/ozone/platform/caca/ozone_platform_caca.cc @@ -16,7 +16,7 @@ #include "ui/ozone/public/gpu_platform_support.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" -#include "ui/ozone/public/ozone_platform.h" // nogncheck +#include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/system_input_injector.h" namespace ui { @@ -57,14 +57,11 @@ class OzonePlatformCaca : public OzonePlatform { delegate, window_manager_.get(), event_source_.get(), bounds)); if (!caca_window->Initialize()) return nullptr; - return caca_window; + return std::move(caca_window); } scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override { return make_scoped_ptr(new NativeDisplayDelegateOzone()); } - base::ScopedFD OpenClientNativePixmapDevice() const override { - return base::ScopedFD(); - } void InitializeUI() override { window_manager_.reset(new CacaWindowManager); diff --git a/chromium/ui/ozone/platform/cast/BUILD.gn b/chromium/ui/ozone/platform/cast/BUILD.gn index f20e5e6257a..53de77f24af 100644 --- a/chromium/ui/ozone/platform/cast/BUILD.gn +++ b/chromium/ui/ozone/platform/cast/BUILD.gn @@ -5,6 +5,11 @@ import("//build/config/chromecast_build.gni") import("//ui/ozone/ozone.gni") +visibility = [ + "//ui/ozone/*", + "//chromecast/*", +] + # GYP version: cast.gypi:ozone_platform_cast # TODO(slan): gn check needs deps on ozone and media to pass. Correct this. source_set("cast") { @@ -32,12 +37,16 @@ source_set("cast") { # do not want to statically link against EGL library. libs = [ "dl" ] + if (disable_display) { + defines = [ "DISABLE_DISPLAY" ] + } + deps = [ "//base", "//chromecast/graphics:libcast_graphics_1.0", - "//chromecast/media/base:message_loop", "//ui/gfx", "//ui/gfx/geometry", "//ui/ozone:ozone_base", + "//ui/ozone/common", ] } diff --git a/chromium/ui/ozone/platform/cast/DEPS b/chromium/ui/ozone/platform/cast/DEPS index 9e7bddfc348..95cbe02f536 100644 --- a/chromium/ui/ozone/platform/cast/DEPS +++ b/chromium/ui/ozone/platform/cast/DEPS @@ -1,4 +1,3 @@ include_rules = [ - "+chromecast/media/base/video_plane_controller.h", "+chromecast/public" ] diff --git a/chromium/ui/ozone/platform/cast/OWNERS b/chromium/ui/ozone/platform/cast/OWNERS index 7513d88f24f..80a937b7403 100644 --- a/chromium/ui/ozone/platform/cast/OWNERS +++ b/chromium/ui/ozone/platform/cast/OWNERS @@ -1,3 +1,3 @@ -gunsch@chromium.org +alokp@chromium.org halliwell@chromium.org lcwu@chromium.org diff --git a/chromium/ui/ozone/platform/cast/cast.gypi b/chromium/ui/ozone/platform/cast/cast.gypi index 82470548508..a0dbd6e1e69 100644 --- a/chromium/ui/ozone/platform/cast/cast.gypi +++ b/chromium/ui/ozone/platform/cast/cast.gypi @@ -4,6 +4,7 @@ { 'variables': { + 'disable_display%': 0, 'internal_ozone_platform_deps': [ 'ozone_platform_cast', ], @@ -18,17 +19,23 @@ 'target_name': 'ozone_platform_cast', 'type': 'static_library', 'dependencies': [ + 'ozone.gyp:ozone_base', + 'ozone.gyp:ozone_common', '../events/events.gyp:events', '../gfx/gfx.gyp:gfx', '../gfx/gfx.gyp:gfx_geometry', '../../base/base.gyp:base', '../../chromecast/chromecast.gyp:cast_public_api', '../../chromecast/chromecast.gyp:libcast_graphics_1.0', - '../../chromecast/media/media.gyp:media_base', ], 'include_dirs': [ '<(DEPTH)/third_party/khronos', ], + 'conditions': [ + ['disable_display==1', { + 'defines': ['DISABLE_DISPLAY'], + }], + ], 'sources': [ 'client_native_pixmap_factory_cast.cc', diff --git a/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc b/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc index 2173d6b1593..e03335f25fc 100644 --- a/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc +++ b/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc @@ -7,7 +7,7 @@ #include "base/logging.h" #include "ui/gfx/buffer_types.h" #include "ui/ozone/public/client_native_pixmap.h" -#include "ui/ozone/public/client_native_pixmap_factory.h" // nogncheck +#include "ui/ozone/public/client_native_pixmap_factory.h" namespace ui { namespace { @@ -29,8 +29,6 @@ class ClientNativePixmapCast : public ClientNativePixmap { class ClientNativePixmapFactoryCast : public ClientNativePixmapFactory { public: // ClientNativePixmapFactoryCast implementation: - void Initialize(base::ScopedFD device_fd) override {} - bool IsConfigurationSupported(gfx::BufferFormat format, gfx::BufferUsage usage) const override { return format == gfx::BufferFormat::RGBA_8888 && diff --git a/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc b/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc index f2618874d03..993da209697 100644 --- a/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc +++ b/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc @@ -4,14 +4,16 @@ #include "ui/ozone/platform/cast/overlay_manager_cast.h" -#include "chromecast/media/base/video_plane_controller.h" -#include "chromecast/public/graphics_types.h" +#include "base/lazy_instance.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/ozone/public/overlay_candidates_ozone.h" namespace ui { namespace { +base::LazyInstance<OverlayManagerCast::OverlayCompositedCallback> + g_overlay_composited_callback = LAZY_INSTANCE_INITIALIZER; + // Translates a gfx::OverlayTransform into a VideoPlane::Transform. // Could be just a lookup table once we have unit tests for this code // to ensure it stays in sync with OverlayTransform. @@ -60,9 +62,9 @@ void OverlayCandidatesCast::CheckOverlaySupport( candidate.display_rect.width(), candidate.display_rect.height()); // Update video plane geometry + transform to match compositor quad. - chromecast::media::VideoPlaneController::GetInstance()->SetGeometry( - display_rect, ConvertTransform(candidate.transform)); - + if (!g_overlay_composited_callback.Get().is_null()) + g_overlay_composited_callback.Get().Run( + display_rect, ConvertTransform(candidate.transform)); return; } } @@ -80,4 +82,10 @@ scoped_ptr<OverlayCandidatesOzone> OverlayManagerCast::CreateOverlayCandidates( return make_scoped_ptr(new OverlayCandidatesCast()); } +// static +void OverlayManagerCast::SetOverlayCompositedCallback( + const OverlayCompositedCallback& cb) { + g_overlay_composited_callback.Get() = cb; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/cast/overlay_manager_cast.h b/chromium/ui/ozone/platform/cast/overlay_manager_cast.h index 808ae3675eb..169f3b1c834 100644 --- a/chromium/ui/ozone/platform/cast/overlay_manager_cast.h +++ b/chromium/ui/ozone/platform/cast/overlay_manager_cast.h @@ -5,13 +5,17 @@ #ifndef UI_OZONE_PLATFORM_CAST_OVERLAY_MANAGER_CAST_H_ #define UI_OZONE_PLATFORM_CAST_OVERLAY_MANAGER_CAST_H_ +#include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "chromecast/public/graphics_types.h" +#include "chromecast/public/video_plane.h" +#include "ui/ozone/ozone_export.h" #include "ui/ozone/public/overlay_manager_ozone.h" namespace ui { -class OverlayManagerCast : public OverlayManagerOzone { +class OZONE_EXPORT OverlayManagerCast : public OverlayManagerOzone { public: OverlayManagerCast(); ~OverlayManagerCast() override; @@ -20,6 +24,14 @@ class OverlayManagerCast : public OverlayManagerOzone { scoped_ptr<OverlayCandidatesOzone> CreateOverlayCandidates( gfx::AcceleratedWidget w) override; + // Callback that's made whenever an overlay quad is processed + // in the compositor. Used to allow hardware video plane to + // be positioned to match compositor hole. + using OverlayCompositedCallback = + base::Callback<void(const chromecast::RectF&, + chromecast::media::VideoPlane::Transform)>; + static void SetOverlayCompositedCallback(const OverlayCompositedCallback& cb); + private: DISALLOW_COPY_AND_ASSIGN(OverlayManagerCast); diff --git a/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc b/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc index bf2bc8306ed..3600efb7072 100644 --- a/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc +++ b/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc @@ -19,7 +19,7 @@ #include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" -#include "ui/ozone/public/ozone_platform.h" // nogncheck +#include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/system_input_injector.h" using chromecast::CastEglPlatform; @@ -74,9 +74,6 @@ class OzonePlatformCast : public OzonePlatform { scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override { return make_scoped_ptr(new NativeDisplayDelegateOzone()); } - base::ScopedFD OpenClientNativePixmapDevice() const override { - return base::ScopedFD(); - } void InitializeUI() override { overlay_manager_.reset(new OverlayManagerCast()); @@ -85,8 +82,15 @@ class OzonePlatformCast : public OzonePlatform { gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); // Enable dummy software rendering support if GPU process disabled + // or if we're an audio-only build. // Note: switch is kDisableGpu from content/public/common/content_switches.h - if (base::CommandLine::ForCurrentProcess()->HasSwitch("disable-gpu")) + bool enable_dummy_software_rendering = true; +#if !defined(DISABLE_DISPLAY) + enable_dummy_software_rendering = + base::CommandLine::ForCurrentProcess()->HasSwitch("disable-gpu"); +#endif + + if (enable_dummy_software_rendering) surface_factory_.reset(new SurfaceFactoryCast()); } void InitializeGPU() override { diff --git a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc index 67478460ba6..72fa4e67901 100644 --- a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc +++ b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc @@ -48,11 +48,11 @@ class DummySurface : public SurfaceOzoneCanvas { ~DummySurface() override {} // SurfaceOzoneCanvas implementation: - skia::RefPtr<SkSurface> GetSurface() override { return surface_; } + sk_sp<SkSurface> GetSurface() override { return surface_; } void ResizeCanvas(const gfx::Size& viewport_size) override { - surface_ = skia::AdoptRef(SkSurface::NewRaster(SkImageInfo::MakeN32Premul( - viewport_size.width(), viewport_size.height()))); + surface_ = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul( + viewport_size.width(), viewport_size.height())); } void PresentCanvas(const gfx::Rect& damage) override {} @@ -62,7 +62,7 @@ class DummySurface : public SurfaceOzoneCanvas { } private: - skia::RefPtr<SkSurface> surface_; + sk_sp<SkSurface> surface_; DISALLOW_COPY_AND_ASSIGN(DummySurface); }; @@ -78,7 +78,9 @@ SurfaceFactoryCast::SurfaceFactoryCast(scoped_ptr<CastEglPlatform> egl_platform) window_(0), display_size_(GetInitialDisplaySize()), new_display_size_(GetInitialDisplaySize()), - egl_platform_(std::move(egl_platform)) {} + egl_platform_(std::move(egl_platform)), + overlay_count_(0), + previous_frame_overlay_count_(0) {} SurfaceFactoryCast::~SurfaceFactoryCast() { ShutdownHardware(); @@ -128,6 +130,31 @@ void SurfaceFactoryCast::ShutdownHardware() { state_ = kUninitialized; } +void SurfaceFactoryCast::OnSwapBuffers() { + DCHECK(overlay_count_ == 0 || overlay_count_ == 1); + + // Logging for overlays to help diagnose bugs when nothing is visible on + // screen. Logging this every frame would be overwhelming, so we just + // log on the transitions from 0 overlays -> 1 overlay and vice versa. + if (overlay_count_ == 0 && previous_frame_overlay_count_ != 0) { + LOG(INFO) << "Overlays deactivated"; + } else if (overlay_count_ != 0 && previous_frame_overlay_count_ == 0) { + LOG(INFO) << "Overlays activated: " << overlay_bounds_.ToString(); + } else if (overlay_count_ == previous_frame_overlay_count_ && + overlay_bounds_ != previous_frame_overlay_bounds_) { + LOG(INFO) << "Overlay geometry changed to " << overlay_bounds_.ToString(); + } + + previous_frame_overlay_count_ = overlay_count_; + previous_frame_overlay_bounds_ = overlay_bounds_; + overlay_count_ = 0; +} + +void SurfaceFactoryCast::OnOverlayScheduled(const gfx::Rect& display_bounds) { + ++overlay_count_; + overlay_bounds_ = display_bounds; +} + scoped_ptr<SurfaceOzoneCanvas> SurfaceFactoryCast::CreateCanvasForWidget( gfx::AcceleratedWidget widget) { // Software canvas support only in headless mode @@ -210,11 +237,6 @@ void SurfaceFactoryCast::ChildDestroyed() { DestroyWindow(); } -const int32_t* SurfaceFactoryCast::GetEGLSurfaceProperties( - const int32_t* desired_list) { - return egl_platform_->GetEGLSurfaceProperties(desired_list); -} - scoped_refptr<NativePixmap> SurfaceFactoryCast::CreateNativePixmap( gfx::AcceleratedWidget widget, gfx::Size size, @@ -222,7 +244,7 @@ scoped_refptr<NativePixmap> SurfaceFactoryCast::CreateNativePixmap( gfx::BufferUsage usage) { class CastPixmap : public NativePixmap { public: - CastPixmap() {} + CastPixmap(SurfaceFactoryCast* parent) : parent_(parent) {} void* GetEGLClientBuffer() const override { // TODO(halliwell): try to implement this through CastEglPlatform. @@ -240,6 +262,7 @@ scoped_refptr<NativePixmap> SurfaceFactoryCast::CreateNativePixmap( gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, const gfx::RectF& crop_rect) override { + parent_->OnOverlayScheduled(display_bounds); return true; } void SetProcessingCallback( @@ -251,9 +274,11 @@ scoped_refptr<NativePixmap> SurfaceFactoryCast::CreateNativePixmap( private: ~CastPixmap() override {} + SurfaceFactoryCast* parent_; + DISALLOW_COPY_AND_ASSIGN(CastPixmap); }; - return make_scoped_refptr(new CastPixmap); + return make_scoped_refptr(new CastPixmap(this)); } bool SurfaceFactoryCast::LoadEGLGLES2Bindings( diff --git a/chromium/ui/ozone/platform/cast/surface_factory_cast.h b/chromium/ui/ozone/platform/cast/surface_factory_cast.h index 33837cda782..fd7ab824f39 100644 --- a/chromium/ui/ozone/platform/cast/surface_factory_cast.h +++ b/chromium/ui/ozone/platform/cast/surface_factory_cast.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/ozone/public/surface_factory_ozone.h" @@ -32,7 +33,6 @@ class SurfaceFactoryCast : public SurfaceFactoryOzone { intptr_t GetNativeDisplay() override; scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget( gfx::AcceleratedWidget widget) override; - const int32_t* GetEGLSurfaceProperties(const int32_t* desired_list) override; scoped_refptr<NativePixmap> CreateNativePixmap( gfx::AcceleratedWidget widget, gfx::Size size, @@ -48,6 +48,10 @@ class SurfaceFactoryCast : public SurfaceFactoryOzone { void TerminateDisplay(); void ShutdownHardware(); + // API for keeping track of overlays per frame for logging purposes + void OnSwapBuffers(); + void OnOverlayScheduled(const gfx::Rect& display_bounds); + private: enum HardwareState { kUninitialized, kInitialized, kFailed }; @@ -64,6 +68,12 @@ class SurfaceFactoryCast : public SurfaceFactoryOzone { gfx::Size new_display_size_; scoped_ptr<chromecast::CastEglPlatform> egl_platform_; + // Overlays scheduled in current and previous frames: + int overlay_count_; + gfx::Rect overlay_bounds_; + int previous_frame_overlay_count_; + gfx::Rect previous_frame_overlay_bounds_; + DISALLOW_COPY_AND_ASSIGN(SurfaceFactoryCast); }; diff --git a/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.cc b/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.cc index bbe8d3c8ad9..d05c1f8c2a7 100644 --- a/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.cc +++ b/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.cc @@ -4,7 +4,9 @@ #include "ui/ozone/platform/cast/surface_ozone_egl_cast.h" +#include "third_party/khronos/EGL/egl.h" #include "ui/gfx/vsync_provider.h" +#include "ui/ozone/common/egl_util.h" #include "ui/ozone/platform/cast/surface_factory_cast.h" namespace ui { @@ -18,11 +20,13 @@ intptr_t SurfaceOzoneEglCast::GetNativeWindow() { } bool SurfaceOzoneEglCast::OnSwapBuffers() { + parent_->OnSwapBuffers(); return true; } void SurfaceOzoneEglCast::OnSwapBuffersAsync( const SwapCompletionCallback& callback) { + parent_->OnSwapBuffers(); callback.Run(gfx::SwapResult::SWAP_ACK); } @@ -34,4 +38,24 @@ scoped_ptr<gfx::VSyncProvider> SurfaceOzoneEglCast::CreateVSyncProvider() { return nullptr; } +void* /* EGLConfig */ SurfaceOzoneEglCast::GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) { + EGLint config_attribs[] = {EGL_BUFFER_SIZE, + 32, + EGL_ALPHA_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_RED_SIZE, + 8, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT, + EGL_NONE}; + return ChooseEGLConfig(egl, config_attribs); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.h b/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.h index 9d19bb6d079..7406cb7fbde 100644 --- a/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.h +++ b/chromium/ui/ozone/platform/cast/surface_ozone_egl_cast.h @@ -24,6 +24,8 @@ class SurfaceOzoneEglCast : public SurfaceOzoneEGL { void OnSwapBuffersAsync(const SwapCompletionCallback& callback) override; bool ResizeNativeWindow(const gfx::Size& viewport_size) override; scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override; + void* /* EGLConfig */ GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) override; private: SurfaceFactoryCast* parent_; diff --git a/chromium/ui/ozone/platform/drm/BUILD.gn b/chromium/ui/ozone/platform/drm/BUILD.gn index 6758a883924..ff90fab2bbb 100644 --- a/chromium/ui/ozone/platform/drm/BUILD.gn +++ b/chromium/ui/ozone/platform/drm/BUILD.gn @@ -6,10 +6,11 @@ import("//build/config/linux/pkg_config.gni") import("//ui/ozone/ozone.gni") declare_args() { - use_mesa_platform_null = false use_drm_atomic = false } +visibility = [ "//ui/ozone/*" ] + pkg_config("libdrm") { packages = [ "libdrm" ] } @@ -24,6 +25,8 @@ source_set("gbm") { sources = [ "client_native_pixmap_factory_gbm.cc", "client_native_pixmap_factory_gbm.h", + "common/client_native_pixmap_dmabuf.cc", + "common/client_native_pixmap_dmabuf.h", "common/drm_util.cc", "common/drm_util.h", "common/scoped_drm_types.cc", @@ -78,6 +81,8 @@ source_set("gbm") { "gpu/hardware_display_plane_manager.h", "gpu/hardware_display_plane_manager_legacy.cc", "gpu/hardware_display_plane_manager_legacy.h", + "gpu/inter_thread_messaging_proxy.cc", + "gpu/inter_thread_messaging_proxy.h", "gpu/overlay_plane.cc", "gpu/overlay_plane.h", "gpu/page_flip_request.cc", @@ -87,19 +92,14 @@ source_set("gbm") { "gpu/scanout_buffer.h", "gpu/screen_manager.cc", "gpu/screen_manager.h", - "host/channel_observer.h", "host/drm_cursor.cc", "host/drm_cursor.h", - "host/drm_cursor_core.cc", - "host/drm_cursor_core.h", "host/drm_device_handle.cc", "host/drm_device_handle.h", "host/drm_display_host.cc", "host/drm_display_host.h", "host/drm_display_host_manager.cc", "host/drm_display_host_manager.h", - "host/drm_display_host_manager_core.cc", - "host/drm_display_host_manager_core.h", "host/drm_gpu_platform_support_host.cc", "host/drm_gpu_platform_support_host.h", "host/drm_native_display_delegate.cc", @@ -112,6 +112,10 @@ source_set("gbm") { "host/drm_window_host.h", "host/drm_window_host_manager.cc", "host/drm_window_host_manager.h", + "host/gpu_thread_adapter.h", + "host/gpu_thread_observer.h", + "mus_thread_proxy.cc", + "mus_thread_proxy.h", "ozone_platform_gbm.cc", "ozone_platform_gbm.h", ] @@ -133,6 +137,8 @@ source_set("gbm") { "//ui/gfx", "//ui/gfx/geometry", "//ui/ozone:ozone_base", + "//ui/ozone/common", + "//ui/platform_window", ] configs += [ ":libdrm" ] @@ -150,15 +156,6 @@ source_set("gbm") { "gpu/hardware_display_plane_manager_atomic.h", ] } - - if (use_vgem_map) { - configs += [ "//ui/ozone:vgem_map" ] - - sources += [ - "common/client_native_pixmap_vgem.cc", - "common/client_native_pixmap_vgem.h", - ] - } } source_set("gbm_unittests") { @@ -189,7 +186,8 @@ source_set("gbm_unittests") { "//skia", "//testing/gtest", "//ui/gfx", - "//ui/ozone", + "//ui/ozone:platform", + "//ui/ozone/common", ] public_configs = [ ":libdrm" ] diff --git a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc index 27bb4a4ca1a..3bae9dd0a57 100644 --- a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc +++ b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc @@ -8,11 +8,8 @@ #include "base/macros.h" #include "ui/gfx/native_pixmap_handle_ozone.h" -#include "ui/ozone/public/client_native_pixmap_factory.h" // nogncheck - -#if defined(USE_VGEM_MAP) -#include "ui/ozone/platform/drm/common/client_native_pixmap_vgem.h" -#endif +#include "ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h" +#include "ui/ozone/public/client_native_pixmap_factory.h" namespace ui { @@ -39,14 +36,6 @@ class ClientNativePixmapFactoryGbm : public ClientNativePixmapFactory { ~ClientNativePixmapFactoryGbm() override {} // ClientNativePixmapFactory: - void Initialize(base::ScopedFD device_fd) override { -#if defined(USE_VGEM_MAP) - // It's called in IO thread. We rely on clients for thread-safety. - // Switching to an IPC message filter ensures thread-safety. - DCHECK_LT(vgem_fd_.get(), 0); - vgem_fd_ = std::move(device_fd); -#endif - } bool IsConfigurationSupported(gfx::BufferFormat format, gfx::BufferUsage usage) const override { switch (usage) { @@ -58,8 +47,8 @@ class ClientNativePixmapFactoryGbm : public ClientNativePixmapFactory { format == gfx::BufferFormat::BGRX_8888; case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: { -#if defined(USE_VGEM_MAP) - return vgem_fd_.is_valid() && format == gfx::BufferFormat::BGRA_8888; +#if defined(OS_CHROMEOS) + return format == gfx::BufferFormat::BGRA_8888; #else return false; #endif @@ -77,15 +66,13 @@ class ClientNativePixmapFactoryGbm : public ClientNativePixmapFactory { switch (usage) { case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: -#if defined(USE_VGEM_MAP) - // A valid |vgem_fd_| is required to acquire a VGEM bo. |vgem_fd_| is - // set before a widget is created. - DCHECK_GE(vgem_fd_.get(), 0); - return ClientNativePixmapVgem::ImportFromDmabuf( - vgem_fd_.get(), scoped_fd.get(), size, handle.stride); -#endif +#if defined(OS_CHROMEOS) + return ClientNativePixmapDmaBuf::ImportFromDmabuf(scoped_fd.release(), + size, handle.stride); +#else NOTREACHED(); return nullptr; +#endif case gfx::BufferUsage::GPU_READ: case gfx::BufferUsage::SCANOUT: return make_scoped_ptr<ClientNativePixmapGbm>( @@ -95,11 +82,6 @@ class ClientNativePixmapFactoryGbm : public ClientNativePixmapFactory { return nullptr; } - private: -#if defined(USE_VGEM_MAP) - base::ScopedFD vgem_fd_; -#endif - DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapFactoryGbm); }; diff --git a/chromium/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.cc b/chromium/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.cc new file mode 100644 index 00000000000..09e8692c1dc --- /dev/null +++ b/chromium/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.cc @@ -0,0 +1,98 @@ +// 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/common/client_native_pixmap_dmabuf.h" + +#include <fcntl.h> +#include <stddef.h> +#include <sys/mman.h> +#include <xf86drm.h> + +#include "base/process/memory.h" +#include "base/trace_event/trace_event.h" + +#if defined(OS_CHROMEOS) +// TODO(vignatti): replace the local definitions below with #include +// <linux/dma-buf.h> once kernel version 4.6 becomes widely used. +#include <linux/types.h> + +struct local_dma_buf_sync { + __u64 flags; +}; + +#define LOCAL_DMA_BUF_SYNC_READ (1 << 0) +#define LOCAL_DMA_BUF_SYNC_WRITE (2 << 0) +#define LOCAL_DMA_BUF_SYNC_START (0 << 2) +#define LOCAL_DMA_BUF_SYNC_END (1 << 2) + +#define LOCAL_DMA_BUF_BASE 'b' +#define LOCAL_DMA_BUF_IOCTL_SYNC \ + _IOW(LOCAL_DMA_BUF_BASE, 0, struct local_dma_buf_sync) +#endif + +namespace ui { + +namespace { + +void PrimeSyncStart(int dmabuf_fd) { + struct local_dma_buf_sync sync_start = {0}; + + sync_start.flags = LOCAL_DMA_BUF_SYNC_START | LOCAL_DMA_BUF_SYNC_READ; + if (drmIoctl(dmabuf_fd, LOCAL_DMA_BUF_IOCTL_SYNC, &sync_start)) + PLOG(ERROR) << "Failed DMA_BUF_SYNC_START"; +} + +void PrimeSyncEnd(int dmabuf_fd) { + struct local_dma_buf_sync sync_end = {0}; + + sync_end.flags = LOCAL_DMA_BUF_SYNC_END | LOCAL_DMA_BUF_SYNC_WRITE; + if (drmIoctl(dmabuf_fd, LOCAL_DMA_BUF_IOCTL_SYNC, &sync_end)) + PLOG(ERROR) << "Failed DMA_BUF_SYNC_END"; +} + +} // namespace + +// static +scoped_ptr<ClientNativePixmap> ClientNativePixmapDmaBuf::ImportFromDmabuf( + int dmabuf_fd, + const gfx::Size& size, + int stride) { + DCHECK_GE(dmabuf_fd, 0); + return make_scoped_ptr(new ClientNativePixmapDmaBuf(dmabuf_fd, size, stride)); +} + +ClientNativePixmapDmaBuf::ClientNativePixmapDmaBuf(int dmabuf_fd, + const gfx::Size& size, + int stride) + : dmabuf_fd_(dmabuf_fd), size_(size), stride_(stride) { + TRACE_EVENT0("drm", "ClientNativePixmapDmaBuf"); + size_t map_size = stride_ * size_.height(); + data_ = mmap(nullptr, map_size, (PROT_READ | PROT_WRITE), MAP_SHARED, + dmabuf_fd, 0); + CHECK_NE(data_, MAP_FAILED); +} + +ClientNativePixmapDmaBuf::~ClientNativePixmapDmaBuf() { + TRACE_EVENT0("drm", "~ClientNativePixmapDmaBuf"); + size_t size = stride_ * size_.height(); + int ret = munmap(data_, size); + DCHECK(!ret); +} + +void* ClientNativePixmapDmaBuf::Map() { + TRACE_EVENT0("drm", "DmaBuf:Map"); + PrimeSyncStart(dmabuf_fd_.get()); + return data_; +} + +void ClientNativePixmapDmaBuf::Unmap() { + TRACE_EVENT0("drm", "DmaBuf:Unmap"); + PrimeSyncEnd(dmabuf_fd_.get()); +} + +void ClientNativePixmapDmaBuf::GetStride(int* stride) const { + *stride = stride_; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h b/chromium/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h new file mode 100644 index 00000000000..b00f34d2124 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h @@ -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. + +#ifndef UI_OZONE_PLATFORM_DRM_COMMON_CLIENT_NATIVE_PIXMAP_DMABUF_H_ +#define UI_OZONE_PLATFORM_DRM_COMMON_CLIENT_NATIVE_PIXMAP_DMABUF_H_ + +#include <stdint.h> + +#include "base/files/scoped_file.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/geometry/size.h" +#include "ui/ozone/public/client_native_pixmap.h" + +namespace ui { + +class ClientNativePixmapDmaBuf : public ClientNativePixmap { + public: + static scoped_ptr<ClientNativePixmap> ImportFromDmabuf(int dmabuf_fd, + const gfx::Size& size, + int stride); + + ~ClientNativePixmapDmaBuf() override; + + // Overridden from ClientNativePixmap. + void* Map() override; + void Unmap() override; + void GetStride(int* stride) const override; + + private: + ClientNativePixmapDmaBuf(int dmabuf_fd, const gfx::Size& size, int stride); + + base::ScopedFD dmabuf_fd_; + const gfx::Size size_; + const int stride_; + void* data_; + + DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapDmaBuf); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_DRM_COMMON_CLIENT_NATIVE_PIXMAP_DMABUF_H_ diff --git a/chromium/ui/ozone/platform/drm/common/client_native_pixmap_vgem.cc b/chromium/ui/ozone/platform/drm/common/client_native_pixmap_vgem.cc deleted file mode 100644 index 6701102848d..00000000000 --- a/chromium/ui/ozone/platform/drm/common/client_native_pixmap_vgem.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/ozone/platform/drm/common/client_native_pixmap_vgem.h" - -#include <fcntl.h> -#include <stddef.h> -#include <sys/mman.h> -#include <vgem_drm.h> -#include <xf86drm.h> - -#include "base/process/memory.h" -#include "base/trace_event/trace_event.h" - -namespace ui { - -// static -scoped_ptr<ClientNativePixmap> ClientNativePixmapVgem::ImportFromDmabuf( - int vgem_fd, - int dmabuf_fd, - const gfx::Size& size, - int stride) { - DCHECK_GE(vgem_fd, 0); - DCHECK_GE(dmabuf_fd, 0); - uint32_t vgem_bo_handle = 0; - int ret = drmPrimeFDToHandle(vgem_fd, dmabuf_fd, &vgem_bo_handle); - DCHECK(!ret) << "drmPrimeFDToHandle failed."; - return make_scoped_ptr( - new ClientNativePixmapVgem(vgem_fd, vgem_bo_handle, size, stride)); -} - -ClientNativePixmapVgem::ClientNativePixmapVgem(int vgem_fd, - uint32_t vgem_bo_handle, - const gfx::Size& size, - int stride) - : vgem_fd_(vgem_fd), - vgem_bo_handle_(vgem_bo_handle), - size_(size), - stride_(stride), - data_(nullptr) { - DCHECK(vgem_bo_handle_); - struct drm_mode_map_dumb mmap_arg = {0}; - mmap_arg.handle = vgem_bo_handle_; - size_t map_size = stride_ * size_.height(); - int ret = drmIoctl(vgem_fd_, DRM_IOCTL_VGEM_MODE_MAP_DUMB, &mmap_arg); - if (ret) { - PLOG(ERROR) << "fail to map a vgem buffer."; - base::TerminateBecauseOutOfMemory(map_size); - } - DCHECK(mmap_arg.offset); - - data_ = mmap(nullptr, map_size, (PROT_READ | PROT_WRITE), MAP_SHARED, - vgem_fd_, mmap_arg.offset); - DCHECK_NE(data_, MAP_FAILED); -} - -ClientNativePixmapVgem::~ClientNativePixmapVgem() { - size_t size = stride_ * size_.height(); - int ret = munmap(data_, size); - DCHECK(!ret) << "fail to munmap a vgem buffer."; - - struct drm_gem_close close = {0}; - close.handle = vgem_bo_handle_; - ret = drmIoctl(vgem_fd_, DRM_IOCTL_GEM_CLOSE, &close); - DCHECK(!ret) << "fail to free a vgem buffer."; -} - -void* ClientNativePixmapVgem::Map() { - return data_; -} - -void ClientNativePixmapVgem::Unmap() {} - -void ClientNativePixmapVgem::GetStride(int* stride) const { - *stride = stride_; -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/common/client_native_pixmap_vgem.h b/chromium/ui/ozone/platform/drm/common/client_native_pixmap_vgem.h deleted file mode 100644 index 4d42cfe9dd2..00000000000 --- a/chromium/ui/ozone/platform/drm/common/client_native_pixmap_vgem.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_OZONE_PLATFORM_DRM_COMMON_CLIENT_NATIVE_PIXMAP_VGEM_H_ -#define UI_OZONE_PLATFORM_DRM_COMMON_CLIENT_NATIVE_PIXMAP_VGEM_H_ - -#include <stdint.h> - -#include "base/file_descriptor_posix.h" -#include "base/macros.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/native_pixmap_handle_ozone.h" -#include "ui/ozone/public/client_native_pixmap.h" - -namespace ui { - -class ClientNativePixmapVgem : public ClientNativePixmap { - public: - static scoped_ptr<ClientNativePixmap> ImportFromDmabuf(int vgem_fd, - int dmabuf_fd, - const gfx::Size& size, - int stride); - - ~ClientNativePixmapVgem() override; - - // Overridden from ClientNativePixmap. - void* Map() override; - void Unmap() override; - void GetStride(int* stride) const override; - - private: - ClientNativePixmapVgem(int vgem_fd, - uint32_t vgem_bo_handle, - const gfx::Size& size, - int stride); - - const int vgem_fd_; - const uint32_t vgem_bo_handle_; - const gfx::Size size_; - const int stride_; - void* data_; - - DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapVgem); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_DRM_COMMON_CLIENT_NATIVE_PIXMAP_VGEM_H_ diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.cc b/chromium/ui/ozone/platform/drm/common/drm_util.cc index dbe061ad96b..586571a1972 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util.cc @@ -157,6 +157,19 @@ int ConnectorIndex(int device_index, int display_index) { return ((device_index << 4) + display_index) & 0xFF; } +bool HasColorCorrectionMatrix(int fd, drmModeCrtc* crtc) { + ScopedDrmObjectPropertyPtr crtc_props( + drmModeObjectGetProperties(fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC)); + + for (uint32_t i = 0; i < crtc_props->count_props; ++i) { + ScopedDrmPropertyPtr property(drmModeGetProperty(fd, crtc_props->props[i])); + if (property && !strcmp(property->name, "CTM")) { + return true; + } + } + return false; +} + } // namespace HardwareDisplayControllerInfo::HardwareDisplayControllerInfo( @@ -229,21 +242,23 @@ DisplaySnapshot_Params CreateDisplaySnapshotParams( params.type = GetDisplayType(info->connector()); params.is_aspect_preserving_scaling = IsAspectPreserving(fd, info->connector()); + params.has_color_correction_matrix = + HasColorCorrectionMatrix(fd, info->crtc()); ScopedDrmPropertyBlobPtr edid_blob( GetDrmPropertyBlob(fd, info->connector(), "EDID")); if (edid_blob) { - std::vector<uint8_t> edid( + params.edid.assign( static_cast<uint8_t*>(edid_blob->data), static_cast<uint8_t*>(edid_blob->data) + edid_blob->length); - GetDisplayIdFromEDID(edid, connector_index, ¶ms.display_id, + GetDisplayIdFromEDID(params.edid, connector_index, ¶ms.display_id, ¶ms.product_id); - ParseOutputDeviceData(edid, nullptr, nullptr, ¶ms.display_name, nullptr, - nullptr); - ParseOutputOverscanFlag(edid, ¶ms.has_overscan); + ParseOutputDeviceData(params.edid, nullptr, nullptr, ¶ms.display_name, + nullptr, nullptr); + ParseOutputOverscanFlag(params.edid, ¶ms.has_overscan); } else { VLOG(1) << "Failed to get EDID blob for connector " << info->connector()->connector_id; diff --git a/chromium/ui/ozone/platform/drm/common/scoped_drm_types.h b/chromium/ui/ozone/platform/drm/common/scoped_drm_types.h index 2fa16df6656..a56f66199fd 100644 --- a/chromium/ui/ozone/platform/drm/common/scoped_drm_types.h +++ b/chromium/ui/ozone/platform/drm/common/scoped_drm_types.h @@ -6,7 +6,6 @@ #define UI_OZONE_PLATFORM_DRM_COMMON_SCOPED_DRM_TYPES_H_ #include "base/memory/scoped_ptr.h" -#include "ui/ozone/ozone_export.h" typedef struct _drmModeConnector drmModeConnector; typedef struct _drmModeCrtc drmModeCrtc; @@ -22,39 +21,39 @@ typedef struct _drmModeRes drmModeRes; namespace ui { -struct OZONE_EXPORT DrmResourcesDeleter { +struct DrmResourcesDeleter { void operator()(drmModeRes* resources) const; }; -struct OZONE_EXPORT DrmConnectorDeleter { +struct DrmConnectorDeleter { void operator()(drmModeConnector* connector) const; }; -struct OZONE_EXPORT DrmCrtcDeleter { +struct DrmCrtcDeleter { void operator()(drmModeCrtc* crtc) const; }; -struct OZONE_EXPORT DrmEncoderDeleter { +struct DrmEncoderDeleter { void operator()(drmModeEncoder* encoder) const; }; -struct OZONE_EXPORT DrmObjectPropertiesDeleter { +struct DrmObjectPropertiesDeleter { void operator()(drmModeObjectProperties* properties) const; }; -struct OZONE_EXPORT DrmPlaneDeleter { +struct DrmPlaneDeleter { void operator()(drmModePlane* plane) const; }; -struct OZONE_EXPORT DrmPlaneResDeleter { +struct DrmPlaneResDeleter { void operator()(drmModePlaneRes* plane_res) const; }; -struct OZONE_EXPORT DrmPropertyDeleter { +struct DrmPropertyDeleter { void operator()(drmModePropertyRes* property) const; }; #if defined(USE_DRM_ATOMIC) -struct OZONE_EXPORT DrmAtomicReqDeleter { +struct DrmAtomicReqDeleter { void operator()(drmModeAtomicReq* property) const; }; #endif // defined(USE_DRM_ATOMIC) -struct OZONE_EXPORT DrmPropertyBlobDeleter { +struct DrmPropertyBlobDeleter { void operator()(drmModePropertyBlobRes* property) const; }; -struct OZONE_EXPORT DrmFramebufferDeleter { +struct DrmFramebufferDeleter { void operator()(drmModeFB* framebuffer) const; }; diff --git a/chromium/ui/ozone/platform/drm/gbm.gypi b/chromium/ui/ozone/platform/drm/gbm.gypi index 26c7029b915..ff131dad935 100644 --- a/chromium/ui/ozone/platform/drm/gbm.gypi +++ b/chromium/ui/ozone/platform/drm/gbm.gypi @@ -13,7 +13,6 @@ 'internal_ozone_platforms': [ 'gbm', ], - 'use_mesa_platform_null%': 0, 'use_drm_atomic%': 0, }, 'targets': [ @@ -34,6 +33,8 @@ 'target_name': 'ozone_platform_gbm', 'type': 'static_library', 'dependencies': [ + 'ozone.gyp:ozone_base', + 'ozone.gyp:ozone_common', '../../base/base.gyp:base', '../../build/linux/system.gyp:libdrm', '../../third_party/minigbm/minigbm.gyp:minigbm', @@ -58,6 +59,8 @@ 'sources': [ 'client_native_pixmap_factory_gbm.cc', 'client_native_pixmap_factory_gbm.h', + 'common/client_native_pixmap_dmabuf.cc', + 'common/client_native_pixmap_dmabuf.h', 'common/drm_util.cc', 'common/drm_util.h', 'common/scoped_drm_types.cc', @@ -113,6 +116,8 @@ 'gpu/hardware_display_plane_manager.h', 'gpu/hardware_display_plane_manager_legacy.cc', 'gpu/hardware_display_plane_manager_legacy.h', + 'gpu/inter_thread_messaging_proxy.cc', + 'gpu/inter_thread_messaging_proxy.h', 'gpu/overlay_plane.cc', 'gpu/overlay_plane.h', 'gpu/page_flip_request.cc', @@ -121,19 +126,14 @@ 'gpu/proxy_helpers.h', 'gpu/screen_manager.cc', 'gpu/screen_manager.h', - 'host/channel_observer.h', 'host/drm_cursor.cc', 'host/drm_cursor.h', - 'host/drm_cursor_core.cc', - 'host/drm_cursor_core.h', 'host/drm_device_handle.cc', 'host/drm_device_handle.h', 'host/drm_display_host.cc', 'host/drm_display_host.h', 'host/drm_display_host_manager.cc', 'host/drm_display_host_manager.h', - 'host/drm_display_host_manager_core.cc', - 'host/drm_display_host_manager_core.h', 'host/drm_gpu_platform_support_host.cc', 'host/drm_gpu_platform_support_host.h', 'host/drm_native_display_delegate.cc', @@ -146,19 +146,14 @@ 'host/drm_window_host.h', 'host/drm_window_host_manager.cc', 'host/drm_window_host_manager.h', + 'host/gpu_thread_adapter.h', + 'host/gpu_thread_observer.h', + 'mus_thread_proxy.cc', + 'mus_thread_proxy.h', 'ozone_platform_gbm.cc', 'ozone_platform_gbm.h', ], 'conditions': [ - ['use_vgem_map==1', { - 'dependencies': [ - '../ozone/ozone.gyp:vgem_map', - ], - 'sources': [ - 'common/client_native_pixmap_vgem.cc', - 'common/client_native_pixmap_vgem.h', - ], - }], ['use_drm_atomic == 1', { 'sources': [ 'gpu/hardware_display_plane_atomic.cc', @@ -177,7 +172,7 @@ '../../skia/skia.gyp:skia', '../gfx/gfx.gyp:gfx', '../gfx/gfx.gyp:gfx_geometry', - 'ozone.gyp:ozone', + 'ozone.gyp:ozone_platform', 'drm_atomic', ], 'export_dependent_settings': [ diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h index 0740ee951c1..f332ce54da6 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h @@ -13,7 +13,6 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "ui/gfx/swap_result.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" #include "ui/ozone/platform/drm/gpu/overlay_plane.h" @@ -28,8 +27,7 @@ class PageFlipRequest; // One CRTC can be paired up with one or more connectors. The simplest // configuration represents one CRTC driving one monitor, while pairing up a // CRTC with multiple connectors results in hardware mirroring. -class OZONE_EXPORT CrtcController - : public base::SupportsWeakPtr<CrtcController> { +class CrtcController : public base::SupportsWeakPtr<CrtcController> { public: CrtcController(const scoped_refptr<DrmDevice>& drm, uint32_t crtc, diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/drm_buffer.cc index bd2e9a93671..1c50f1c4724 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_buffer.cc @@ -38,8 +38,6 @@ DrmBuffer::DrmBuffer(const scoped_refptr<DrmDevice>& drm) : drm_(drm) { } DrmBuffer::~DrmBuffer() { - surface_.clear(); - if (framebuffer_ && !drm_->RemoveFramebuffer(framebuffer_)) PLOG(ERROR) << "DrmBuffer: RemoveFramebuffer: fb " << framebuffer_; @@ -78,8 +76,7 @@ bool DrmBuffer::Initialize(const SkImageInfo& info, } } - surface_ = - skia::AdoptRef(SkSurface::NewRasterDirect(info, mmap_base_, stride_)); + surface_ = SkSurface::MakeRasterDirect(info, mmap_base_, stride_); if (!surface_) { LOG(ERROR) << "DrmBuffer: Failed to create SkSurface: handle " << handle_; return false; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.h b/chromium/ui/ozone/platform/drm/gpu/drm_buffer.h index 61d553e644e..726cf11934e 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_buffer.h @@ -11,7 +11,6 @@ #include "base/macros.h" #include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkSurface.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/gpu/scanout_buffer.h" namespace ui { @@ -21,7 +20,7 @@ class DrmDevice; // Wrapper for a DRM allocated buffer. Keeps track of the native properties of // the buffer and wraps the pixel memory into a SkSurface which can be used to // draw into using Skia. -class OZONE_EXPORT DrmBuffer : public ScanoutBuffer { +class DrmBuffer : public ScanoutBuffer { public: DrmBuffer(const scoped_refptr<DrmDevice>& drm); @@ -65,7 +64,7 @@ class OZONE_EXPORT DrmBuffer : public ScanoutBuffer { uint32_t fb_pixel_format_ = 0; // Wrapper around the native pixel memory. - skia::RefPtr<SkSurface> surface_; + sk_sp<SkSurface> surface_; DISALLOW_COPY_AND_ASSIGN(DrmBuffer); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.cc index 464c3805269..0fe4399d979 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.cc @@ -44,8 +44,7 @@ bool DrmConsoleBuffer::Initialize() { return false; } - surface_ = - skia::AdoptRef(SkSurface::NewRasterDirect(info, mmap_base_, stride_)); + surface_ = SkSurface::MakeRasterDirect(info, mmap_base_, stride_); if (!surface_) return false; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.h b/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.h index 2f28b9e6585..250ca3b54e5 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.h @@ -40,7 +40,7 @@ class DrmConsoleBuffer { scoped_refptr<DrmDevice> drm_; // Wrapper around the native pixel memory. - skia::RefPtr<SkSurface> surface_; + sk_sp<SkSurface> surface_; // Length of a row of pixels. uint32_t stride_ = 0; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc index 3a30fd23e5a..891a136c8f8 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc @@ -13,7 +13,9 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/memory/free_deleter.h" #include "base/message_loop/message_loop.h" +#include "base/posix/safe_strerror.h" #include "base/task_runner.h" #include "base/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" @@ -113,6 +115,161 @@ bool CanQueryForResources(int fd) { return !drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &resources); } +// TODO(robert.bradford): Replace with libdrm structures after libdrm roll. +// https://crbug.com/586475 +struct DrmColorLut { + uint16_t red; + uint16_t green; + uint16_t blue; + uint16_t reserved; +}; + +struct DrmColorCtm { + int64_t ctm_coeff[9]; +}; + +struct DrmModeCreateBlob { + uint64_t data; + uint32_t length; + uint32_t blob_id; +}; + +struct DrmModeDestroyBlob { + uint32_t blob_id; +}; + +#ifndef DRM_IOCTL_MODE_CREATEPROPBLOB +#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct DrmModeCreateBlob) +#endif + +#ifndef DRM_IOCTL_MODE_DESTROYPROPBLOB +#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct DrmModeDestroyBlob) +#endif + +int CreatePropertyBlob(int fd, const void* data, size_t length, uint32_t* id) { + DrmModeCreateBlob create; + int ret; + + if (length >= 0xffffffff) + return -ERANGE; + + memset(&create, 0, sizeof(create)); + + create.length = length; + create.data = (uintptr_t)data; + create.blob_id = 0; + *id = 0; + + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &create); + ret = ret < 0 ? -errno : ret; + if (ret != 0) + return ret; + + *id = create.blob_id; + return 0; +} + +int DestroyPropertyBlob(int fd, uint32_t id) { + DrmModeDestroyBlob destroy; + int ret; + + memset(&destroy, 0, sizeof(destroy)); + destroy.blob_id = id; + ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy); + return ret < 0 ? -errno : ret; +} + +using ScopedDrmColorLutPtr = scoped_ptr<DrmColorLut, base::FreeDeleter>; +using ScopedDrmColorCtmPtr = scoped_ptr<DrmColorCtm, base::FreeDeleter>; + +ScopedDrmColorLutPtr CreateLutBlob( + const std::vector<GammaRampRGBEntry>& source) { + TRACE_EVENT0("drm", "CreateLutBlob"); + ScopedDrmColorLutPtr lut( + static_cast<DrmColorLut*>(malloc(sizeof(DrmColorLut) * source.size()))); + DrmColorLut* p = lut.get(); + for (size_t i = 0; i < source.size(); ++i) { + p[i].red = source[i].r; + p[i].green = source[i].g; + p[i].blue = source[i].b; + } + return lut; +} + +ScopedDrmColorCtmPtr CreateCTMBlob( + const std::vector<float>& correction_matrix) { + ScopedDrmColorCtmPtr ctm( + static_cast<DrmColorCtm*>(malloc(sizeof(DrmColorCtm)))); + for (size_t i = 0; i < arraysize(ctm->ctm_coeff); ++i) { + if (correction_matrix[i] < 0) { + ctm->ctm_coeff[i] = static_cast<uint64_t>( + -correction_matrix[i] * (static_cast<uint64_t>(1) << 32)); + ctm->ctm_coeff[i] |= static_cast<uint64_t>(1) << 63; + } else { + ctm->ctm_coeff[i] = static_cast<uint64_t>( + correction_matrix[i] * (static_cast<uint64_t>(1) << 32)); + } + } + return ctm; +} + +bool SetBlobProperty(int fd, + uint32_t object_id, + uint32_t object_type, + uint32_t prop_id, + const char* property_name, + unsigned char* data, + size_t length) { + uint32_t blob_id; + int res; + res = CreatePropertyBlob(fd, data, length, &blob_id); + if (res != 0) { + LOG(ERROR) << "Error creating property blob: " << base::safe_strerror(res) + << " for property " << property_name; + return false; + } + res = drmModeObjectSetProperty(fd, object_id, object_type, prop_id, blob_id); + if (res != 0) { + LOG(ERROR) << "Error updating property: " << base::safe_strerror(res) + << " for property " << property_name; + DestroyPropertyBlob(fd, blob_id); + return false; + } + DestroyPropertyBlob(fd, blob_id); + return true; +} + +std::vector<GammaRampRGBEntry> ResampleLut( + const std::vector<GammaRampRGBEntry>& lut_in, + size_t desired_size) { + TRACE_EVENT1("drm", "ResampleLut", "desired_size", desired_size); + if (lut_in.size() == desired_size) + return lut_in; + + std::vector<GammaRampRGBEntry> result; + result.resize(desired_size); + + for (size_t i = 0; i < desired_size; ++i) { + size_t base_index = lut_in.size() * i / desired_size; + size_t remaining = lut_in.size() * i % desired_size; + if (base_index < lut_in.size() - 1) { + result[i].r = lut_in[base_index].r + + (lut_in[base_index + 1].r - lut_in[base_index].r) * + remaining / desired_size; + result[i].g = lut_in[base_index].g + + (lut_in[base_index + 1].g - lut_in[base_index].g) * + remaining / desired_size; + result[i].b = lut_in[base_index].b + + (lut_in[base_index + 1].b - lut_in[base_index].b) * + remaining / desired_size; + } else { + result[i] = lut_in[lut_in.size() - 1]; + } + } + + return result; +} + } // namespace class DrmDevice::PageFlipManager { @@ -542,4 +699,74 @@ bool DrmDevice::SetGammaRamp(uint32_t crtc_id, &g[0], &b[0]) == 0); } +bool DrmDevice::SetColorCorrection( + uint32_t crtc_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + ScopedDrmObjectPropertyPtr crtc_props(drmModeObjectGetProperties( + file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC)); + uint64_t degamma_lut_size = 0; + uint64_t gamma_lut_size = 0; + + for (uint32_t i = 0; i < crtc_props->count_props; ++i) { + ScopedDrmPropertyPtr property( + drmModeGetProperty(file_.GetPlatformFile(), crtc_props->props[i])); + if (property && !strcmp(property->name, "DEGAMMA_LUT_SIZE")) { + degamma_lut_size = crtc_props->prop_values[i]; + } + if (property && !strcmp(property->name, "GAMMA_LUT_SIZE")) { + gamma_lut_size = crtc_props->prop_values[i]; + } + + if (degamma_lut_size && gamma_lut_size) + break; + } + + if (degamma_lut_size == 0 || gamma_lut_size == 0) { + LOG(WARNING) << "No available (de)gamma tables."; + return false; + } + + ScopedDrmColorLutPtr degamma_blob_data = + CreateLutBlob(ResampleLut(degamma_lut, degamma_lut_size)); + ScopedDrmColorLutPtr gamma_blob_data = + CreateLutBlob(ResampleLut(gamma_lut, gamma_lut_size)); + ScopedDrmColorCtmPtr ctm_blob_data = CreateCTMBlob(correction_matrix); + + for (uint32_t i = 0; i < crtc_props->count_props; ++i) { + ScopedDrmPropertyPtr property( + drmModeGetProperty(file_.GetPlatformFile(), crtc_props->props[i])); + if (!property) + continue; + + if (!strcmp(property->name, "DEGAMMA_LUT")) { + if (!SetBlobProperty( + file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC, + crtc_props->props[i], property->name, + reinterpret_cast<unsigned char*>(degamma_blob_data.get()), + sizeof(DrmColorLut) * degamma_lut_size)) + return false; + } + if (!strcmp(property->name, "GAMMA_LUT")) { + if (!SetBlobProperty( + file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC, + crtc_props->props[i], property->name, + reinterpret_cast<unsigned char*>(gamma_blob_data.get()), + sizeof(DrmColorLut) * gamma_lut_size)) + return false; + } + if (!strcmp(property->name, "CTM")) { + if (!SetBlobProperty( + file_.GetPlatformFile(), crtc_id, DRM_MODE_OBJECT_CRTC, + crtc_props->props[i], property->name, + reinterpret_cast<unsigned char*>(ctm_blob_data.get()), + sizeof(DrmColorCtm))) + return false; + } + } + + return true; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.h b/chromium/ui/ozone/platform/drm/gpu/drm_device.h index 3d4e8186df4..ef863c1e510 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.h @@ -18,7 +18,6 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/overlay_transform.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" @@ -39,7 +38,7 @@ struct GammaRampRGBEntry; // Wraps DRM calls into a nice interface. Used to provide different // implementations of the DRM calls. For the actual implementation the DRM API // would be called. In unit tests this interface would be stubbed. -class OZONE_EXPORT DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { +class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { public: typedef base::Callback<void(unsigned int /* frame */, unsigned int /* seconds */, @@ -164,6 +163,11 @@ class OZONE_EXPORT DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { // Set the gamma ramp for |crtc_id| to reflect the ramps in |lut|. virtual bool SetGammaRamp(uint32_t crtc_id, const std::vector<GammaRampRGBEntry>& lut); + virtual bool SetColorCorrection( + uint32_t crtc_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix); virtual bool SetCapability(uint64_t capability, uint64_t value); 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 c4f3304931b..d05221dac81 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc @@ -101,6 +101,10 @@ scoped_refptr<DrmDevice> DrmDeviceManager::GetDrmDevice( return it->second; } +scoped_refptr<DrmDevice> DrmDeviceManager::GetPrimaryDrmDevice() { + return primary_device_; +} + const DrmDeviceVector& DrmDeviceManager::GetDrmDevices() const { return devices_; } 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 d415aad1fcb..a893f09e2ad 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h @@ -12,7 +12,6 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "ui/gfx/native_widget_types.h" -#include "ui/ozone/ozone_export.h" namespace base { class FilePath; @@ -28,7 +27,7 @@ typedef std::vector<scoped_refptr<DrmDevice>> DrmDeviceVector; // Tracks the mapping between widgets and the DRM devices used to allocate // buffers for the window represented by the widget. -class OZONE_EXPORT DrmDeviceManager { +class DrmDeviceManager { public: DrmDeviceManager(scoped_ptr<DrmDeviceGenerator> drm_device_generator); ~DrmDeviceManager(); @@ -48,6 +47,9 @@ class OZONE_EXPORT DrmDeviceManager { // returns |primary_device_|. scoped_refptr<DrmDevice> GetDrmDevice(gfx::AcceleratedWidget widget); + // Returns |primary_device_|. + scoped_refptr<DrmDevice> GetPrimaryDrmDevice(); + const DrmDeviceVector& GetDrmDevices() const; private: diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc index 076cc59d356..6ff1033cffb 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc @@ -170,10 +170,21 @@ bool DrmDisplay::SetHDCPState(HDCPState state) { GetContentProtectionValue(hdcp_property.get(), state)); } -void DrmDisplay::SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut) { - if (!drm_->SetGammaRamp(crtc_, lut)) { - LOG(ERROR) << "Failed to set gamma ramp for display: crtc_id = " << crtc_ - << " size = " << lut.size(); +void DrmDisplay::SetColorCorrection( + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + if (degamma_lut.empty()) { + if (!drm_->SetGammaRamp(crtc_, gamma_lut)) { + LOG(ERROR) << "Failed to set gamma ramp for display: crtc_id = " << crtc_ + << " size = " << gamma_lut.size(); + } + } else { + if (!drm_->SetColorCorrection(crtc_, degamma_lut, gamma_lut, + correction_matrix)) { + LOG(ERROR) << "Failed to set color correction for display: crtc_id = " + << crtc_; + } } } diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.h b/chromium/ui/ozone/platform/drm/gpu/drm_display.h index 86021ae05bf..3742eda146c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.h @@ -44,7 +44,9 @@ class DrmDisplay { bool Configure(const drmModeModeInfo* mode, const gfx::Point& origin); bool GetHDCPState(HDCPState* state); bool SetHDCPState(HDCPState state); - void SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut); + void SetColorCorrection(const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix); private: ScreenManager* screen_manager_; // Not owned. diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc index 402d3c04ca7..da787239ba2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc @@ -99,6 +99,17 @@ std::vector<DisplaySnapshot_Params> DrmGpuDisplayManager::GetDisplays() { return params_list; } +void DrmGpuDisplayManager::GetScanoutFormats( + gfx::AcceleratedWidget widget, + std::vector<gfx::BufferFormat>* scanout_formats) { + const std::vector<uint32_t>& fourcc_formats = + drm_device_manager_->GetDrmDevice(widget) + ->plane_manager() + ->GetSupportedFormats(); + for (auto& fourcc : fourcc_formats) + scanout_formats->push_back(GetBufferFormatFromFourCCFormat(fourcc)); +} + bool DrmGpuDisplayManager::TakeDisplayControl() { const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices(); bool status = true; @@ -184,16 +195,18 @@ bool DrmGpuDisplayManager::SetHDCPState(int64_t display_id, HDCPState state) { return display->SetHDCPState(state); } -void DrmGpuDisplayManager::SetGammaRamp( +void DrmGpuDisplayManager::SetColorCorrection( int64_t display_id, - const std::vector<GammaRampRGBEntry>& lut) { + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { DrmDisplay* display = FindDisplay(display_id); if (!display) { LOG(ERROR) << "There is no display with ID " << display_id; return; } - display->SetGammaRamp(lut); + display->SetColorCorrection(degamma_lut, gamma_lut, correction_matrix); } DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) { diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h index 681a7cdec48..23e80e66f71 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h @@ -29,6 +29,11 @@ class DrmGpuDisplayManager { // displays is refreshed. std::vector<DisplaySnapshot_Params> GetDisplays(); + // Returns all scanout formats for |widget| representing a particular display + // controller or default display controller for kNullAcceleratedWidget. + void GetScanoutFormats(gfx::AcceleratedWidget widget, + std::vector<gfx::BufferFormat>* scanout_formats); + // Takes/releases the control of the DRM devices. bool TakeDisplayControl(); void RelinquishDisplayControl(); @@ -40,6 +45,10 @@ class DrmGpuDisplayManager { bool GetHDCPState(int64_t display_id, HDCPState* state); bool SetHDCPState(int64_t display_id, HDCPState state); void SetGammaRamp(int64_t id, const std::vector<GammaRampRGBEntry>& lut); + void SetColorCorrection(int64_t id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix); private: DrmDisplay* FindDisplay(int64_t display_id); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc index 1a66c814b44..9fb93b9e242 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc @@ -69,7 +69,6 @@ uint32_t FindOptimalBufferFormat(uint32_t original_format, const gfx::Rect& window_bounds, HardwareDisplayController* controller) { bool force_primary_format = false; - uint32_t optimal_format = original_format; uint32_t z_order = plane_z_order; // If Overlay completely covers primary and isn't transparent, try to find // optimal format w.r.t primary plane. This guarantees that optimal format @@ -85,17 +84,17 @@ uint32_t FindOptimalBufferFormat(uint32_t original_format, #endif } - if (force_primary_format) { - optimal_format = DRM_FORMAT_XRGB8888; - } else { - if (controller->IsFormatSupported(DRM_FORMAT_UYVY, z_order)) { - optimal_format = DRM_FORMAT_UYVY; - } else if (controller->IsFormatSupported(DRM_FORMAT_XRGB8888, z_order)) { - optimal_format = DRM_FORMAT_XRGB8888; - } + if (force_primary_format) + return DRM_FORMAT_XRGB8888; + + // YUV is preferable format if supported. + if (controller->IsFormatSupported(DRM_FORMAT_UYVY, z_order)) { + return DRM_FORMAT_UYVY; + } else if (controller->IsFormatSupported(DRM_FORMAT_XRGB8888, z_order)) { + return DRM_FORMAT_XRGB8888; } - return optimal_format; + return original_format; } } // namespace @@ -142,9 +141,10 @@ std::vector<OverlayCheck_Params> DrmOverlayValidator::TestPageFlip( gfx::Size scaled_buffer_size = GetScaledSize( overlay.buffer_size, overlay.display_rect, overlay.crop_rect); - scoped_refptr<ScanoutBuffer> buffer = GetBufferForPageFlipTest( - drm, scaled_buffer_size, GetFourCCFormatForFramebuffer(overlay.format), - buffer_generator_, &reusable_buffers); + uint32_t original_format = GetFourCCFormatForFramebuffer(overlay.format); + scoped_refptr<ScanoutBuffer> buffer = + GetBufferForPageFlipTest(drm, scaled_buffer_size, original_format, + buffer_generator_, &reusable_buffers); DCHECK(buffer); OverlayPlane plane(buffer, overlay.plane_z_order, overlay.transform, @@ -153,6 +153,37 @@ std::vector<OverlayCheck_Params> DrmOverlayValidator::TestPageFlip( if (controller->TestPageFlip(test_list)) { overlay.is_overlay_candidate = true; + + // If size scaling is needed, find an optimal format. + if (overlay.plane_z_order && scaled_buffer_size != overlay.buffer_size) { + uint32_t optimal_format = FindOptimalBufferFormat( + original_format, overlay.plane_z_order, overlay.display_rect, + window_->bounds(), controller); + + if (original_format != optimal_format) { + OverlayPlane original_plain = test_list.back(); + test_list.pop_back(); + scoped_refptr<ScanoutBuffer> optimal_buffer = + GetBufferForPageFlipTest(drm, scaled_buffer_size, optimal_format, + buffer_generator_, &reusable_buffers); + DCHECK(optimal_buffer); + + OverlayPlane optimal_plane(optimal_buffer, overlay.plane_z_order, + overlay.transform, overlay.display_rect, + overlay.crop_rect); + test_list.push_back(optimal_plane); + + // If test failed here, it means even though optimal_format is + // supported, platform cannot support it with current combination of + // layers. This is usually the case when optimal_format needs certain + // capabilites (i.e. conversion, scaling etc) and needed hardware + // resources might be already in use. Fall back to original format. + if (!controller->TestPageFlip(test_list)) { + test_list.pop_back(); + test_list.push_back(original_plain); + } + } + } } else { // If test failed here, platform cannot support this configuration // with current combination of layers. This is usually the case when this @@ -166,7 +197,7 @@ std::vector<OverlayCheck_Params> DrmOverlayValidator::TestPageFlip( } } - UpdateOverlayHintsCache(drm, test_list, &reusable_buffers); + UpdateOverlayHintsCache(test_list); return validated_params; } @@ -208,7 +239,8 @@ OverlayPlaneList DrmOverlayValidator::PrepareBuffersForPageFlip( target_size = original_size; } - if (original_size != target_size || original_format != target_format) { + // The size scaling piggybacks the format conversion. + if (original_size != target_size) { scoped_refptr<ScanoutBuffer> processed_buffer = plane.processing_callback.Run(target_size, target_format); @@ -225,58 +257,26 @@ void DrmOverlayValidator::ClearCache() { } void DrmOverlayValidator::UpdateOverlayHintsCache( - const scoped_refptr<DrmDevice>& drm, - const OverlayPlaneList& plane_list, - std::vector<scoped_refptr<ScanoutBuffer>>* reusable_buffers) { + const OverlayPlaneList& plane_list) { const auto& iter = overlay_hints_cache_.Get(plane_list); if (iter != overlay_hints_cache_.end()) return; - OverlayPlaneList preferred_format_test_list = plane_list; - HardwareDisplayController* controller = window_->GetController(); + OverlayPlaneList hints_plane_list = plane_list; OverlayHintsList overlay_hints; - for (auto& plane : preferred_format_test_list) { - uint32_t original_format = plane.buffer->GetFramebufferPixelFormat(); - - if (plane.z_order == 0) { - overlay_hints.push_back( - OverlayHints(original_format, true /* scaling */)); - continue; - } - - uint32_t optimal_format = FindOptimalBufferFormat( - original_format, plane.z_order, plane.display_bounds, window_->bounds(), - controller); - - if (optimal_format != original_format) { - scoped_refptr<ScanoutBuffer> original_buffer = plane.buffer; - plane.buffer = - GetBufferForPageFlipTest(drm, plane.buffer->GetSize(), optimal_format, - buffer_generator_, reusable_buffers); - - if (!controller->TestPageFlip(preferred_format_test_list)) { - // If test failed here, it means even though optimal_format is - // supported, platform cannot support it with current combination of - // layers. This is usually the case when optimal_format needs certain - // capabilites (i.e. conversion, scaling etc) and needed hardware - // resources might be already in use. Fall back to original format. - optimal_format = original_format; - plane.buffer = original_buffer; - } - } - + for (auto& plane : hints_plane_list) { + uint32_t format = plane.buffer->GetFramebufferPixelFormat(); // TODO(kalyank): We always request scaling to be done by 3D engine, VPP // etc. We should use them only if downscaling is needed and let display // controller handle up-scaling on platforms which support it. - overlay_hints.push_back(OverlayHints(optimal_format, true /* scaling */)); - } + overlay_hints.push_back(OverlayHints(format, true /* scaling */)); - // Make sure we dont hold reference to buffer when caching this plane list. - for (auto& plane : preferred_format_test_list) + // Make sure we dont hold reference to buffer when caching this plane list. plane.buffer = nullptr; + } - DCHECK(preferred_format_test_list.size() == overlay_hints.size()); - overlay_hints_cache_.Put(preferred_format_test_list, overlay_hints); + DCHECK(hints_plane_list.size() == overlay_hints.size()); + overlay_hints_cache_.Put(hints_plane_list, overlay_hints); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h index bf09c75a3b9..29b52351390 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h @@ -59,21 +59,14 @@ class DrmOverlayValidator { using OverlayHintsList = std::vector<OverlayHints>; - // Check if we can optimize format for reducing Display controller read - // bandwidth for |plane_list| and cache the value. - void UpdateOverlayHintsCache( - const scoped_refptr<DrmDevice>& drm, - const OverlayPlaneList& plane_list, - std::vector<scoped_refptr<ScanoutBuffer>>* reusable_buffers); + // Update hints cache. + void UpdateOverlayHintsCache(const OverlayPlaneList& plane_list); DrmWindow* window_; // Not owned. ScanoutBufferGenerator* buffer_generator_; // Not owned. // List of all configurations which have been validated. - base::MRUCacheBase<OverlayPlaneList, - OverlayHintsList, - base::MRUCacheNullDeletor<OverlayHintsList>> - overlay_hints_cache_; + base::MRUCache<OverlayPlaneList, OverlayHintsList> overlay_hints_cache_; DISALLOW_COPY_AND_ASSIGN(DrmOverlayValidator); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc index f39d5f6af5d..66221599587 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc @@ -157,16 +157,16 @@ TEST_F(DrmOverlayValidatorTest, WindowWithNoController) { window_->SetController(nullptr); std::vector<ui::OverlayCheck_Params> validated_params = overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); - EXPECT_EQ(false, validated_params.front().is_overlay_candidate); - EXPECT_EQ(false, validated_params.back().is_overlay_candidate); + EXPECT_FALSE(validated_params.front().is_overlay_candidate); + EXPECT_FALSE(validated_params.back().is_overlay_candidate); window_->SetController(controller); } TEST_F(DrmOverlayValidatorTest, DontPromoteMoreLayersThanAvailablePlanes) { std::vector<ui::OverlayCheck_Params> validated_params = overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); - EXPECT_EQ(true, validated_params.front().is_overlay_candidate); - EXPECT_EQ(false, validated_params.back().is_overlay_candidate); + EXPECT_TRUE(validated_params.front().is_overlay_candidate); + EXPECT_FALSE(validated_params.back().is_overlay_candidate); } TEST_F(DrmOverlayValidatorTest, DontCollapseOverlayToPrimaryInFullScreen) { @@ -179,8 +179,8 @@ TEST_F(DrmOverlayValidatorTest, DontCollapseOverlayToPrimaryInFullScreen) { overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); // Second candidate should be marked as Invalid as we have only one plane // per CRTC. - EXPECT_EQ(true, validated_params.front().is_overlay_candidate); - EXPECT_EQ(false, validated_params.back().is_overlay_candidate); + EXPECT_TRUE(validated_params.front().is_overlay_candidate); + EXPECT_FALSE(validated_params.back().is_overlay_candidate); } TEST_F(DrmOverlayValidatorTest, ClearCacheOnReset) { @@ -202,6 +202,40 @@ TEST_F(DrmOverlayValidatorTest, ClearCacheOnReset) { ui::OverlayPlaneList plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); + EXPECT_EQ(DRM_FORMAT_XRGB8888, + plane_list.back().buffer->GetFramebufferPixelFormat()); + // Check if ClearCache actually clears the cache. + overlay_validator_->ClearCache(); + plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); + // There should be no entry in cache for this configuration and should return + // default value of DRM_FORMAT_XRGB8888. + EXPECT_EQ(DRM_FORMAT_XRGB8888, + plane_list.back().buffer->GetFramebufferPixelFormat()); +} + +TEST_F(DrmOverlayValidatorTest, ClearCacheOnResetWithScaling) { + // This test checks if we invalidate cache when Reset is called. + gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); + overlay_params_.back().buffer_size = overlay_rect_.size(); + overlay_params_.back().display_rect = overlay_rect_; + overlay_params_.back().crop_rect = crop_rect; + plane_list_.back().display_bounds = overlay_rect_; + plane_list_.back().crop_rect = crop_rect; + std::vector<uint32_t> xrgb_yuv_packed_formats = {DRM_FORMAT_XRGB8888, + DRM_FORMAT_UYVY}; + + ui::FakePlaneInfo primary_plane_info( + 100, 1 << 0, std::vector<uint32_t>(1, DRM_FORMAT_XRGB8888)); + ui::FakePlaneInfo overlay_info(101, 1 << 0, xrgb_yuv_packed_formats); + std::vector<ui::FakePlaneInfo> planes_info{primary_plane_info, overlay_info}; + plane_manager_->SetPlaneProperties(planes_info); + overlay_validator_->ClearCache(); + + overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); + + ui::OverlayPlaneList plane_list = + overlay_validator_->PrepareBuffersForPageFlip(plane_list_); + // Scaling allows format conversion. EXPECT_EQ(DRM_FORMAT_UYVY, plane_list.back().buffer->GetFramebufferPixelFormat()); // Check if ClearCache actually clears the cache. @@ -267,13 +301,47 @@ TEST_F(DrmOverlayValidatorTest, OptimalFormatForOverlayInFullScreen_YUV) { #endif } +TEST_F(DrmOverlayValidatorTest, OverlayPreferredFormat) { + plane_manager_->ResetPlaneCount(); + // This test checks for optimal format in case of non full screen video case. + overlay_params_.back().buffer_size = overlay_rect_.size(); + overlay_params_.back().display_rect = overlay_rect_; + plane_list_.back().display_bounds = overlay_rect_; + + std::vector<uint32_t> xrgb_yuv_packed_formats = {DRM_FORMAT_XRGB8888, + DRM_FORMAT_UYVY}; + ui::FakePlaneInfo primary_plane_info( + 100, 1 << 0, std::vector<uint32_t>(1, DRM_FORMAT_XRGB8888)); + ui::FakePlaneInfo overlay_info(101, 1 << 0, xrgb_yuv_packed_formats); + std::vector<ui::FakePlaneInfo> planes_info{primary_plane_info, overlay_info}; + plane_manager_->SetPlaneProperties(planes_info); + overlay_validator_->ClearCache(); + + std::vector<ui::OverlayCheck_Params> validated_params = + overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); + + for (const auto& param : validated_params) + EXPECT_TRUE(param.is_overlay_candidate); + + EXPECT_EQ(3, plane_manager_->plane_count()); + + ui::OverlayPlaneList plane_list = + overlay_validator_->PrepareBuffersForPageFlip(plane_list_); + EXPECT_EQ(DRM_FORMAT_XRGB8888, + plane_list.back().buffer->GetFramebufferPixelFormat()); +} + TEST_F(DrmOverlayValidatorTest, OverlayPreferredFormat_YUV) { plane_manager_->ResetPlaneCount(); // This test checks for optimal format in case of non full screen video case. - // Prefer YUV as optimal format when Overlay supports it. + // Prefer YUV as optimal format when Overlay supports it and scaling is + // needed. + gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); overlay_params_.back().buffer_size = overlay_rect_.size(); overlay_params_.back().display_rect = overlay_rect_; + overlay_params_.back().crop_rect = crop_rect; plane_list_.back().display_bounds = overlay_rect_; + plane_list_.back().crop_rect = crop_rect; std::vector<uint32_t> xrgb_yuv_packed_formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}; @@ -288,7 +356,7 @@ TEST_F(DrmOverlayValidatorTest, OverlayPreferredFormat_YUV) { overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); for (const auto& param : validated_params) - EXPECT_EQ(true, param.is_overlay_candidate); + EXPECT_TRUE(param.is_overlay_candidate); EXPECT_EQ(5, plane_manager_->plane_count()); @@ -323,7 +391,7 @@ TEST_F(DrmOverlayValidatorTest, OverlayPreferredFormat_XRGB) { plane_list.back().buffer->GetFramebufferPixelFormat()); EXPECT_EQ(3, plane_manager_->plane_count()); for (const auto& param : validated_params) - EXPECT_EQ(true, param.is_overlay_candidate); + EXPECT_TRUE(param.is_overlay_candidate); } TEST_F(DrmOverlayValidatorTest, RejectYUVBuffersIfNotSupported) { @@ -349,7 +417,7 @@ TEST_F(DrmOverlayValidatorTest, RejectYUVBuffersIfNotSupported) { validated_params = overlay_validator_->TestPageFlip(validated_params, ui::OverlayPlaneList()); - EXPECT_EQ(false, validated_params.back().is_overlay_candidate); + EXPECT_FALSE(validated_params.back().is_overlay_candidate); } TEST_F(DrmOverlayValidatorTest, @@ -367,9 +435,12 @@ TEST_F(DrmOverlayValidatorTest, new ui::MockScanoutBuffer(primary_rect_.size()))); EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode)); + gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); overlay_params_.back().buffer_size = overlay_rect_.size(); overlay_params_.back().display_rect = overlay_rect_; + overlay_params_.back().crop_rect = crop_rect; plane_list_.back().display_bounds = overlay_rect_; + plane_list_.back().crop_rect = crop_rect; ui::FakePlaneInfo primary_crtc_primary_plane(100, 1 << 0, only_rgb_format); ui::FakePlaneInfo primary_crtc_overlay(101, 1 << 0, xrgb_yuv_packed_formats); @@ -389,10 +460,10 @@ TEST_F(DrmOverlayValidatorTest, validated_params = overlay_validator_->TestPageFlip(validated_params, ui::OverlayPlaneList()); - EXPECT_EQ(true, validated_params.back().is_overlay_candidate); + EXPECT_TRUE(validated_params.back().is_overlay_candidate); - // Both controllers have Overlay which support DRM_FORMAT_UYVY, hence this - // should be picked as the optimal format. + // Both controllers have Overlay which support DRM_FORMAT_UYVY, and scaling is + // needed, hence this should be picked as the optimal format. ui::OverlayPlaneList plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); EXPECT_EQ(DRM_FORMAT_UYVY, @@ -408,7 +479,7 @@ TEST_F(DrmOverlayValidatorTest, validated_params = overlay_validator_->TestPageFlip(validated_params, ui::OverlayPlaneList()); - EXPECT_EQ(false, validated_params.back().is_overlay_candidate); + EXPECT_FALSE(validated_params.back().is_overlay_candidate); // Check case where we dont have support for packed formats in primary // display. @@ -419,7 +490,7 @@ TEST_F(DrmOverlayValidatorTest, validated_params = overlay_validator_->TestPageFlip(validated_params, ui::OverlayPlaneList()); - EXPECT_EQ(false, validated_params.back().is_overlay_candidate); + EXPECT_FALSE(validated_params.back().is_overlay_candidate); controller->RemoveCrtc(drm_, kSecondaryCrtc); } @@ -458,17 +529,12 @@ TEST_F(DrmOverlayValidatorTest, OptimalFormatYUV_MirroredControllers) { std::vector<ui::OverlayCheck_Params> validated_params = overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); - EXPECT_EQ(true, validated_params.back().is_overlay_candidate); - // Both controllers have Overlay which support DRM_FORMAT_UYVY, hence this - // should be picked as the optimal format. + EXPECT_TRUE(validated_params.back().is_overlay_candidate); ui::OverlayPlaneList plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); - EXPECT_EQ(DRM_FORMAT_UYVY, + EXPECT_EQ(DRM_FORMAT_XRGB8888, plane_list.back().buffer->GetFramebufferPixelFormat()); - // DRM_FORMAT_XRGB8888 should be the preferred format when either of the - // controllers dont support UYVY format. - // Check case where we dont have support for packed formats in Mirrored CRTC. planes_info.back().allowed_formats = only_rgb_format; plane_manager_->SetPlaneProperties(planes_info); @@ -476,7 +542,7 @@ TEST_F(DrmOverlayValidatorTest, OptimalFormatYUV_MirroredControllers) { validated_params = overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); - EXPECT_EQ(true, validated_params.back().is_overlay_candidate); + EXPECT_TRUE(validated_params.back().is_overlay_candidate); plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); EXPECT_EQ(DRM_FORMAT_XRGB8888, @@ -491,7 +557,7 @@ TEST_F(DrmOverlayValidatorTest, OptimalFormatYUV_MirroredControllers) { validated_params = overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); - EXPECT_EQ(true, validated_params.back().is_overlay_candidate); + EXPECT_TRUE(validated_params.back().is_overlay_candidate); plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); EXPECT_EQ(DRM_FORMAT_XRGB8888, @@ -502,9 +568,12 @@ TEST_F(DrmOverlayValidatorTest, OptimalFormatYUV_MirroredControllers) { TEST_F(DrmOverlayValidatorTest, OptimizeOnlyIfProcessingCallbackPresent) { // This test checks that we dont manipulate overlay buffers in case Processing // callback is not present. + gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); overlay_params_.back().buffer_size = overlay_rect_.size(); overlay_params_.back().display_rect = overlay_rect_; + overlay_params_.back().crop_rect = crop_rect; plane_list_.back().display_bounds = overlay_rect_; + plane_list_.back().crop_rect = crop_rect; std::vector<uint32_t> xrgb_yuv_packed_formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}; @@ -519,6 +588,7 @@ TEST_F(DrmOverlayValidatorTest, OptimizeOnlyIfProcessingCallbackPresent) { ui::OverlayPlaneList plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); + // Scaling allows format conversion. EXPECT_EQ(DRM_FORMAT_UYVY, plane_list.back().buffer->GetFramebufferPixelFormat()); plane_list_.back().processing_callback.Reset(); @@ -531,9 +601,12 @@ TEST_F(DrmOverlayValidatorTest, OptimizeOnlyIfProcessingCallbackPresent) { TEST_F(DrmOverlayValidatorTest, DontResetOriginalBufferIfProcessedIsInvalid) { // This test checks that we dont manipulate overlay buffers in case Processing // callback is not present. + gfx::RectF crop_rect = gfx::RectF(0, 0, 0.5, 0.5); overlay_params_.back().buffer_size = overlay_rect_.size(); overlay_params_.back().display_rect = overlay_rect_; + overlay_params_.back().crop_rect = crop_rect; plane_list_.back().display_bounds = overlay_rect_; + plane_list_.back().crop_rect = crop_rect; std::vector<uint32_t> xrgb_yuv_packed_formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}; @@ -548,6 +621,7 @@ TEST_F(DrmOverlayValidatorTest, DontResetOriginalBufferIfProcessedIsInvalid) { ui::OverlayPlaneList plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); + // Scaling allows format conversion. EXPECT_EQ(DRM_FORMAT_UYVY, plane_list.back().buffer->GetFramebufferPixelFormat()); plane_list_.back().processing_callback = base::Bind( diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc index 63ec6f96db2..20952051b2f 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc @@ -76,7 +76,10 @@ DrmThread::~DrmThread() { } void DrmThread::Start() { - if (!StartWithOptions(base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) + base::Thread::Options thread_options; + thread_options.message_loop_type = base::MessageLoop::TYPE_IO; + thread_options.priority = base::ThreadPriority::DISPLAY; + if (!StartWithOptions(thread_options)) LOG(FATAL) << "Failed to create DRM thread"; } @@ -106,6 +109,24 @@ void DrmThread::CreateBuffer(gfx::AcceleratedWidget widget, *buffer = GbmBuffer::CreateBuffer(gbm, format, size, usage); } +void DrmThread::CreateBufferFromFD(const gfx::Size& size, + gfx::BufferFormat format, + base::ScopedFD fd, + int32_t stride, + scoped_refptr<GbmBuffer>* buffer) { + scoped_refptr<GbmDevice> gbm = + static_cast<GbmDevice*>(device_manager_->GetPrimaryDrmDevice().get()); + DCHECK(gbm); + *buffer = + GbmBuffer::CreateBufferFromFD(gbm, format, size, std::move(fd), stride); +} + +void DrmThread::GetScanoutFormats( + gfx::AcceleratedWidget widget, + std::vector<gfx::BufferFormat>* scanout_formats) { + display_manager_->GetScanoutFormats(widget, scanout_formats); +} + void DrmThread::SchedulePageFlip(gfx::AcceleratedWidget widget, const std::vector<OverlayPlane>& planes, const SwapCompletionCallback& callback) { @@ -221,9 +242,13 @@ void DrmThread::SetHDCPState( callback.Run(display_id, display_manager_->SetHDCPState(display_id, state)); } -void DrmThread::SetGammaRamp(int64_t id, - const std::vector<GammaRampRGBEntry>& lut) { - display_manager_->SetGammaRamp(id, lut); +void DrmThread::SetColorCorrection( + int64_t display_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + display_manager_->SetColorCorrection(display_id, degamma_lut, gamma_lut, + correction_matrix); } } // 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 130b0dc4e33..49e55865072 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h @@ -58,7 +58,14 @@ class DrmThread : public base::Thread { gfx::BufferFormat format, gfx::BufferUsage usage, scoped_refptr<GbmBuffer>* buffer); - + void CreateBufferFromFD(const gfx::Size& size, + gfx::BufferFormat format, + base::ScopedFD fd, + int stride, + scoped_refptr<GbmBuffer>* buffer); + + void GetScanoutFormats(gfx::AcceleratedWidget widget, + std::vector<gfx::BufferFormat>* scanout_formats); void SchedulePageFlip(gfx::AcceleratedWidget widget, const std::vector<OverlayPlane>& planes, const SwapCompletionCallback& callback); @@ -102,7 +109,10 @@ class DrmThread : public base::Thread { void SetHDCPState(int64_t display_id, HDCPState state, const base::Callback<void(int64_t, bool)>& callback); - void SetGammaRamp(int64_t id, const std::vector<GammaRampRGBEntry>& lut); + void SetColorCorrection(int64_t display_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix); // base::Thread: void Init() override; 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 f0647eb7085..92baacafd05 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 @@ -13,11 +13,14 @@ namespace ui { -DrmThreadMessageProxy::DrmThreadMessageProxy(DrmThread* drm_thread) - : drm_thread_(drm_thread), weak_ptr_factory_(this) {} +DrmThreadMessageProxy::DrmThreadMessageProxy() : weak_ptr_factory_(this) {} DrmThreadMessageProxy::~DrmThreadMessageProxy() {} +void DrmThreadMessageProxy::SetDrmThread(DrmThread* thread) { + drm_thread_ = thread; +} + void DrmThreadMessageProxy::OnFilterAdded(IPC::Sender* sender) { sender_ = sender; @@ -51,10 +54,10 @@ bool DrmThreadMessageProxy::OnMessageReceived(const IPC::Message& message) { OnRemoveGraphicsDevice) IPC_MESSAGE_HANDLER(OzoneGpuMsg_GetHDCPState, OnGetHDCPState) IPC_MESSAGE_HANDLER(OzoneGpuMsg_SetHDCPState, OnSetHDCPState) - IPC_MESSAGE_HANDLER(OzoneGpuMsg_SetGammaRamp, OnSetGammaRamp); + IPC_MESSAGE_HANDLER(OzoneGpuMsg_SetColorCorrection, OnSetColorCorrection) IPC_MESSAGE_HANDLER(OzoneGpuMsg_CheckOverlayCapabilities, OnCheckOverlayCapabilities) - IPC_MESSAGE_UNHANDLED(handled = false); + IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -209,13 +212,16 @@ void DrmThreadMessageProxy::OnSetHDCPState(int64_t display_id, display_id, state, CreateSafeCallback(callback))); } -void DrmThreadMessageProxy::OnSetGammaRamp( +void DrmThreadMessageProxy::OnSetColorCorrection( int64_t id, - const std::vector<GammaRampRGBEntry>& lut) { + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::SetGammaRamp, - base::Unretained(drm_thread_), id, lut)); + FROM_HERE, + base::Bind(&DrmThread::SetColorCorrection, base::Unretained(drm_thread_), + id, degamma_lut, gamma_lut, correction_matrix)); } void DrmThreadMessageProxy::OnCheckOverlayCapabilitiesCallback( diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h index 6aa90627bdb..1e031516f27 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 @@ -13,6 +13,7 @@ #include "ui/display/types/display_constants.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" #include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h" namespace base { struct FileDescriptor; @@ -31,9 +32,13 @@ struct DisplayMode_Params; struct DisplaySnapshot_Params; struct OverlayCheck_Params; -class DrmThreadMessageProxy : public IPC::MessageFilter { +class DrmThreadMessageProxy : public IPC::MessageFilter, + public InterThreadMessagingProxy { public: - DrmThreadMessageProxy(DrmThread* drm_thread); + DrmThreadMessageProxy(); + + // InterThreadMessagingProxy. + void SetDrmThread(DrmThread* thread) override; // IPC::MessageFilter: void OnFilterAdded(IPC::Sender* sender) override; @@ -68,7 +73,10 @@ class DrmThreadMessageProxy : public IPC::MessageFilter { void OnRemoveGraphicsDevice(const base::FilePath& path); void OnGetHDCPState(int64_t display_id); void OnSetHDCPState(int64_t display_id, HDCPState state); - void OnSetGammaRamp(int64_t id, const std::vector<GammaRampRGBEntry>& lut); + void OnSetColorCorrection(int64_t id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix); void OnCheckOverlayCapabilitiesCallback( gfx::AcceleratedWidget widget, 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 b46e11624ab..208c895e6f0 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc @@ -16,9 +16,9 @@ DrmThreadProxy::DrmThreadProxy() {} DrmThreadProxy::~DrmThreadProxy() {} -scoped_refptr<DrmThreadMessageProxy> -DrmThreadProxy::CreateDrmThreadMessageProxy() { - return make_scoped_refptr(new DrmThreadMessageProxy(&drm_thread_)); +void DrmThreadProxy::BindThreadIntoMessagingProxy( + InterThreadMessagingProxy* messaging_proxy) { + messaging_proxy->SetDrmThread(&drm_thread_); } scoped_ptr<DrmWindowProxy> DrmThreadProxy::CreateDrmWindowProxy( @@ -39,4 +39,26 @@ scoped_refptr<GbmBuffer> DrmThreadProxy::CreateBuffer( return buffer; } +scoped_refptr<GbmBuffer> DrmThreadProxy::CreateBufferFromFD( + const gfx::Size& size, + gfx::BufferFormat format, + base::ScopedFD fd, + int stride) { + scoped_refptr<GbmBuffer> buffer; + PostSyncTask( + drm_thread_.task_runner(), + base::Bind(&DrmThread::CreateBufferFromFD, base::Unretained(&drm_thread_), + size, format, base::Passed(&fd), stride, &buffer)); + return buffer; +} + +void DrmThreadProxy::GetScanoutFormats( + gfx::AcceleratedWidget widget, + std::vector<gfx::BufferFormat>* scanout_formats) { + PostSyncTask( + drm_thread_.task_runner(), + base::Bind(&DrmThread::GetScanoutFormats, base::Unretained(&drm_thread_), + widget, scanout_formats)); +} + } // 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 05e4b530039..213e0527bf1 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h @@ -12,8 +12,8 @@ namespace ui { -class DrmThreadMessageProxy; class DrmWindowProxy; +class InterThreadMessagingProxy; // Mediates the communication between GPU main/IO threads and the DRM thread. It // serves proxy objects that are safe to call on the GPU threads. The proxy @@ -23,7 +23,7 @@ class DrmThreadProxy { explicit DrmThreadProxy(); ~DrmThreadProxy(); - scoped_refptr<DrmThreadMessageProxy> CreateDrmThreadMessageProxy(); + void BindThreadIntoMessagingProxy(InterThreadMessagingProxy* messaging_proxy); scoped_ptr<DrmWindowProxy> CreateDrmWindowProxy( gfx::AcceleratedWidget widget); @@ -33,6 +33,14 @@ class DrmThreadProxy { gfx::BufferFormat format, gfx::BufferUsage usage); + scoped_refptr<GbmBuffer> CreateBufferFromFD(const gfx::Size& size, + gfx::BufferFormat format, + base::ScopedFD fd, + int stride); + + void GetScanoutFormats(gfx::AcceleratedWidget widget, + std::vector<gfx::BufferFormat>* scanout_formats); + private: DrmThread drm_thread_; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window.h b/chromium/ui/ozone/platform/drm/gpu/drm_window.h index f5dad7c28da..59407a57043 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window.h @@ -14,7 +14,6 @@ #include "ui/gfx/native_widget_types.h" #include "ui/gfx/swap_result.h" #include "ui/gfx/vsync_provider.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/gpu/overlay_plane.h" #include "ui/ozone/platform/drm/gpu/page_flip_request.h" #include "ui/ozone/public/surface_ozone_egl.h" @@ -46,7 +45,7 @@ class ScreenManager; // // If there's no display whose bounds match the window's, the window is // disconnected and its contents will not be visible to the user. -class OZONE_EXPORT DrmWindow { +class DrmWindow { public: DrmWindow(gfx::AcceleratedWidget widget, DrmDeviceManager* device_manager, diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc index ccda65bde6e..25d89dc464d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc @@ -34,9 +34,9 @@ const uint32_t kDefaultCrtc = 1; const uint32_t kDefaultConnector = 2; const int kDefaultCursorSize = 64; -std::vector<skia::RefPtr<SkSurface>> GetCursorBuffers( +std::vector<sk_sp<SkSurface>> GetCursorBuffers( const scoped_refptr<ui::MockDrmDevice> drm) { - std::vector<skia::RefPtr<SkSurface>> cursor_buffers; + std::vector<sk_sp<SkSurface>> cursor_buffers; for (const auto& cursor_buffer : drm->buffers()) { if (cursor_buffer->width() == kDefaultCursorSize && cursor_buffer->height() == kDefaultCursorSize) { @@ -120,7 +120,7 @@ TEST_F(DrmWindowTest, SetCursorImage) { gfx::Point(4, 2), 0); SkBitmap cursor; - std::vector<skia::RefPtr<SkSurface>> cursor_buffers = GetCursorBuffers(drm_); + std::vector<sk_sp<SkSurface>> cursor_buffers = GetCursorBuffers(drm_); EXPECT_EQ(2u, cursor_buffers.size()); // Buffers 1 is the cursor backbuffer we just drew in. diff --git a/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.cc b/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.cc index 3695383e53e..2e9a2a732d8 100644 --- a/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.cc +++ b/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.cc @@ -18,6 +18,8 @@ FakePlaneInfo::FakePlaneInfo(uint32_t plane_id, const std::vector<uint32_t>& formats) : id(plane_id), allowed_crtc_mask(crtc_mask), allowed_formats(formats) {} +FakePlaneInfo::FakePlaneInfo(const FakePlaneInfo& other) = default; + FakePlaneInfo::~FakePlaneInfo() {} } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.h b/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.h index d9c9ea88987..79265ebe534 100644 --- a/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.h +++ b/chromium/ui/ozone/platform/drm/gpu/fake_plane_info.h @@ -18,6 +18,7 @@ struct FakePlaneInfo { FakePlaneInfo(uint32_t plane_id, uint32_t crtc_mask, const std::vector<uint32_t>& formats); + FakePlaneInfo(const FakePlaneInfo& other); ~FakePlaneInfo(); uint32_t id; diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc index 336045dc8db..437ceabc0dc 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc @@ -20,7 +20,7 @@ #include "ui/ozone/platform/drm/gpu/gbm_device.h" #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h" #include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h" -#include "ui/ozone/public/ozone_platform.h" // nogncheck +#include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/surface_factory_ozone.h" namespace ui { @@ -28,14 +28,28 @@ namespace ui { GbmBuffer::GbmBuffer(const scoped_refptr<GbmDevice>& gbm, gbm_bo* bo, gfx::BufferFormat format, - gfx::BufferUsage usage) - : GbmBufferBase(gbm, bo, format, usage), format_(format), usage_(usage) {} + gfx::BufferUsage usage, + base::ScopedFD fd, + int stride) + : GbmBufferBase(gbm, bo, format, usage), + format_(format), + usage_(usage), + fd_(std::move(fd)), + stride_(stride) {} GbmBuffer::~GbmBuffer() { if (bo()) gbm_bo_destroy(bo()); } +int GbmBuffer::GetFd() const { + return fd_.get(); +} + +int GbmBuffer::GetStride() const { + return stride_; +} + // static scoped_refptr<GbmBuffer> GbmBuffer::CreateBuffer( const scoped_refptr<GbmDevice>& gbm, @@ -44,44 +58,61 @@ scoped_refptr<GbmBuffer> GbmBuffer::CreateBuffer( gfx::BufferUsage usage) { TRACE_EVENT2("drm", "GbmBuffer::CreateBuffer", "device", gbm->device_path().value(), "size", size.ToString()); - bool use_scanout = (usage == gfx::BufferUsage::SCANOUT); + unsigned flags = 0; - // GBM_BO_USE_SCANOUT is the hint of x-tiling. - if (use_scanout) - flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + switch (usage) { + case gfx::BufferUsage::GPU_READ: + break; + case gfx::BufferUsage::SCANOUT: + flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + break; + case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: + case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: + flags = GBM_BO_USE_LINEAR; + break; + } + gbm_bo* bo = gbm_bo_create(gbm->device(), size.width(), size.height(), GetFourCCFormatFromBufferFormat(format), flags); if (!bo) return nullptr; - scoped_refptr<GbmBuffer> buffer(new GbmBuffer(gbm, bo, format, usage)); - if (use_scanout && !buffer->GetFramebufferId()) + // The fd returned by gbm_bo_get_fd is not ref-counted and need to be + // kept open for the lifetime of the buffer. + base::ScopedFD fd(gbm_bo_get_fd(bo)); + if (!fd.is_valid()) { + PLOG(ERROR) << "Failed to export buffer to dma_buf"; + gbm_bo_destroy(bo); + return nullptr; + } + + scoped_refptr<GbmBuffer> buffer(new GbmBuffer( + gbm, bo, format, usage, std::move(fd), gbm_bo_get_stride(bo))); + if (usage == gfx::BufferUsage::SCANOUT && !buffer->GetFramebufferId()) return nullptr; return buffer; } -GbmPixmap::GbmPixmap(GbmSurfaceFactory* surface_manager) - : surface_manager_(surface_manager) {} +// static +scoped_refptr<GbmBuffer> GbmBuffer::CreateBufferFromFD( + const scoped_refptr<GbmDevice>& gbm, + gfx::BufferFormat format, + const gfx::Size& size, + base::ScopedFD fd, + int stride) { + TRACE_EVENT2("drm", "GbmBuffer::CreateBufferFromFD", "device", + gbm->device_path().value(), "size", size.ToString()); -void GbmPixmap::Initialize(base::ScopedFD dma_buf, int dma_buf_pitch) { - dma_buf_ = std::move(dma_buf); - dma_buf_pitch_ = dma_buf_pitch; + // TODO(reveman): Use gbm_bo_import after making buffers survive + // GPU process crashes. crbug.com/597932 + return make_scoped_refptr(new GbmBuffer( + gbm, nullptr, format, gfx::BufferUsage::GPU_READ, std::move(fd), stride)); } -bool GbmPixmap::InitializeFromBuffer(const scoped_refptr<GbmBuffer>& buffer) { - // We want to use the GBM API because it's going to call into libdrm - // which might do some optimizations on buffer allocation, - // especially when sharing buffers via DMABUF. - base::ScopedFD dma_buf(gbm_bo_get_fd(buffer->bo())); - if (!dma_buf.is_valid()) { - PLOG(ERROR) << "Failed to export buffer to dma_buf"; - return false; - } - Initialize(std::move(dma_buf), gbm_bo_get_stride(buffer->bo())); - buffer_ = buffer; - return true; -} +GbmPixmap::GbmPixmap(GbmSurfaceFactory* surface_manager, + const scoped_refptr<GbmBuffer>& buffer) + : surface_manager_(surface_manager), buffer_(buffer) {} void GbmPixmap::SetProcessingCallback( const ProcessingCallback& processing_callback) { @@ -92,14 +123,14 @@ void GbmPixmap::SetProcessingCallback( gfx::NativePixmapHandle GbmPixmap::ExportHandle() { gfx::NativePixmapHandle handle; - base::ScopedFD dmabuf_fd(HANDLE_EINTR(dup(dma_buf_.get()))); - if (!dmabuf_fd.is_valid()) { + base::ScopedFD fd(HANDLE_EINTR(dup(buffer_->GetFd()))); + if (!fd.is_valid()) { PLOG(ERROR) << "dup"; return handle; } - handle.fd = base::FileDescriptor(dmabuf_fd.release(), true /* auto_close */); - handle.stride = dma_buf_pitch_; + handle.fd = base::FileDescriptor(fd.release(), true /* auto_close */); + handle.stride = buffer_->GetStride(); return handle; } @@ -111,11 +142,11 @@ void* GbmPixmap::GetEGLClientBuffer() const { } int GbmPixmap::GetDmaBufFd() const { - return dma_buf_.get(); + return buffer_->GetFd(); } int GbmPixmap::GetDmaBufPitch() const { - return dma_buf_pitch_; + return buffer_->GetStride(); } gfx::BufferFormat GbmPixmap::GetBufferFormat() const { @@ -131,16 +162,14 @@ bool GbmPixmap::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, const gfx::RectF& crop_rect) { - // TODO(reveman): Add support for imported buffers. crbug.com/541558 - if (!buffer_) { - PLOG(ERROR) << "ScheduleOverlayPlane requires a buffer."; - return false; - } - DCHECK(buffer_->GetUsage() == gfx::BufferUsage::SCANOUT); + OverlayPlane::ProcessBufferCallback processing_callback; + if (!processing_callback_.is_null()) + processing_callback = base::Bind(&GbmPixmap::ProcessBuffer, this); + surface_manager_->GetSurface(widget)->QueueOverlayPlane( OverlayPlane(buffer_, plane_z_order, plane_transform, display_bounds, - crop_rect, base::Bind(&GbmPixmap::ProcessBuffer, this))); + crop_rect, processing_callback)); return true; } @@ -158,13 +187,13 @@ scoped_refptr<ScanoutBuffer> GbmPixmap::ProcessBuffer(const gfx::Size& size, scoped_refptr<GbmBuffer> buffer = GbmBuffer::CreateBuffer( buffer_->drm().get(), buffer_format, size, buffer_->GetUsage()); + if (!buffer) + return nullptr; // ProcessBuffer is called on DrmThread. We could have used // CreateNativePixmap to initialize the pixmap, however it posts a // synchronous task to DrmThread resulting in a deadlock. - processed_pixmap_ = new GbmPixmap(surface_manager_); - if (!processed_pixmap_->InitializeFromBuffer(buffer)) - return nullptr; + processed_pixmap_ = new GbmPixmap(surface_manager_, buffer); } DCHECK(!processing_callback_.is_null()); diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h index ff42e1b841a..b62ccfcc1d8 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h @@ -26,27 +26,39 @@ class GbmBuffer : public GbmBufferBase { gfx::BufferFormat format, const gfx::Size& size, gfx::BufferUsage usage); + static scoped_refptr<GbmBuffer> CreateBufferFromFD( + const scoped_refptr<GbmDevice>& gbm, + gfx::BufferFormat format, + const gfx::Size& size, + base::ScopedFD fd, + int stride); gfx::BufferFormat GetFormat() const { return format_; } gfx::BufferUsage GetUsage() const { return usage_; } + int GetFd() const; + int GetStride() const; private: GbmBuffer(const scoped_refptr<GbmDevice>& gbm, gbm_bo* bo, gfx::BufferFormat format, - gfx::BufferUsage usage); + gfx::BufferUsage usage, + base::ScopedFD fd, + int stride); ~GbmBuffer() override; gfx::BufferFormat format_; gfx::BufferUsage usage_; + base::ScopedFD fd_; + int stride_; DISALLOW_COPY_AND_ASSIGN(GbmBuffer); }; class GbmPixmap : public NativePixmap { public: - explicit GbmPixmap(GbmSurfaceFactory* surface_manager); - void Initialize(base::ScopedFD dma_buf, int dma_buf_pitch); - bool InitializeFromBuffer(const scoped_refptr<GbmBuffer>& buffer); + GbmPixmap(GbmSurfaceFactory* surface_manager, + const scoped_refptr<GbmBuffer>& buffer); + void SetProcessingCallback( const ProcessingCallback& processing_callback) override; @@ -70,11 +82,8 @@ class GbmPixmap : public NativePixmap { scoped_refptr<ScanoutBuffer> ProcessBuffer(const gfx::Size& size, uint32_t format); - scoped_refptr<GbmBuffer> buffer_; - base::ScopedFD dma_buf_; - int dma_buf_pitch_ = -1; - GbmSurfaceFactory* surface_manager_; + scoped_refptr<GbmBuffer> buffer_; // OverlayValidator can request scaling or format conversions as needed for // this Pixmap. This holds the processed buffer. diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc index fb9157bc9a8..32762ee73de 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc @@ -10,6 +10,7 @@ #include "build/build_config.h" #include "third_party/khronos/EGL/egl.h" #include "ui/ozone/common/egl_util.h" +#include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h" #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" #include "ui/ozone/platform/drm/gpu/gbm_buffer.h" @@ -53,28 +54,6 @@ intptr_t GbmSurfaceFactory::GetNativeDisplay() { return EGL_DEFAULT_DISPLAY; } -const int32_t* GbmSurfaceFactory::GetEGLSurfaceProperties( - const int32_t* desired_list) { - DCHECK(thread_checker_.CalledOnValidThread()); - static const int32_t kConfigAttribs[] = {EGL_BUFFER_SIZE, - 32, - EGL_ALPHA_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_RED_SIZE, - 8, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, - EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, - EGL_NONE}; - - return kConfigAttribs; -} - bool GbmSurfaceFactory::LoadEGLGLES2Bindings( AddGLLibraryCallback add_gl_library, SetGLGetProcAddressProcCallback set_gl_get_proc_address) { @@ -103,6 +82,13 @@ GbmSurfaceFactory::CreateSurfacelessEGLSurfaceForWidget( new GbmSurfaceless(drm_thread_->CreateDrmWindowProxy(widget), this)); } +std::vector<gfx::BufferFormat> GbmSurfaceFactory::GetScanoutFormats( + gfx::AcceleratedWidget widget) { + std::vector<gfx::BufferFormat> scanout_formats; + drm_thread_->GetScanoutFormats(widget, &scanout_formats); + return scanout_formats; +} + scoped_refptr<ui::NativePixmap> GbmSurfaceFactory::CreateNativePixmap( gfx::AcceleratedWidget widget, gfx::Size size, @@ -110,7 +96,7 @@ scoped_refptr<ui::NativePixmap> GbmSurfaceFactory::CreateNativePixmap( gfx::BufferUsage usage) { #if !defined(OS_CHROMEOS) // Support for memory mapping accelerated buffers requires some - // CrOS-specific patches (using vgem). + // CrOS-specific patches (using dma-buf mmap API). DCHECK(gfx::BufferUsage::SCANOUT == usage); #endif @@ -119,18 +105,19 @@ scoped_refptr<ui::NativePixmap> GbmSurfaceFactory::CreateNativePixmap( if (!buffer.get()) return nullptr; - scoped_refptr<GbmPixmap> pixmap(new GbmPixmap(this)); - if (!pixmap->InitializeFromBuffer(buffer)) - return nullptr; - - return pixmap; + return make_scoped_refptr(new GbmPixmap(this, buffer)); } scoped_refptr<ui::NativePixmap> GbmSurfaceFactory::CreateNativePixmapFromHandle( + gfx::Size size, + gfx::BufferFormat format, const gfx::NativePixmapHandle& handle) { - scoped_refptr<GbmPixmap> pixmap(new GbmPixmap(this)); - pixmap->Initialize(base::ScopedFD(handle.fd.fd), handle.stride); - return pixmap; + scoped_refptr<GbmBuffer> buffer = drm_thread_->CreateBufferFromFD( + size, format, base::ScopedFD(handle.fd.fd), handle.stride); + if (!buffer) + return nullptr; + + return make_scoped_refptr(new GbmPixmap(this, buffer)); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h index 5beb886865d..0fb582466d0 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h @@ -8,6 +8,7 @@ #include <stdint.h> #include <map> +#include <vector> #include "base/macros.h" #include "base/threading/thread_checker.h" @@ -21,7 +22,7 @@ class GbmSurfaceless; class GbmSurfaceFactory : public SurfaceFactoryOzone { public: - GbmSurfaceFactory(DrmThreadProxy* drm_thread); + explicit GbmSurfaceFactory(DrmThreadProxy* drm_thread); ~GbmSurfaceFactory() override; void RegisterSurface(gfx::AcceleratedWidget widget, GbmSurfaceless* surface); @@ -30,7 +31,8 @@ class GbmSurfaceFactory : public SurfaceFactoryOzone { // SurfaceFactoryOzone: intptr_t GetNativeDisplay() override; - const int32_t* GetEGLSurfaceProperties(const int32_t* desired_list) override; + std::vector<gfx::BufferFormat> GetScanoutFormats( + gfx::AcceleratedWidget widget) override; bool LoadEGLGLES2Bindings( AddGLLibraryCallback add_gl_library, SetGLGetProcAddressProcCallback set_gl_get_proc_address) override; @@ -46,6 +48,8 @@ class GbmSurfaceFactory : public SurfaceFactoryOzone { gfx::BufferFormat format, gfx::BufferUsage usage) override; scoped_refptr<NativePixmap> CreateNativePixmapFromHandle( + gfx::Size size, + gfx::BufferFormat format, const gfx::NativePixmapHandle& handle) override; private: diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index 500d74c4cdb..f5512b757ae 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc @@ -6,6 +6,9 @@ #include <utility> +#include "base/trace_event/trace_event.h" +#include "third_party/khronos/EGL/egl.h" +#include "ui/ozone/common/egl_util.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h" #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" @@ -44,6 +47,7 @@ bool GbmSurfaceless::OnSwapBuffers() { void GbmSurfaceless::OnSwapBuffersAsync( const SwapCompletionCallback& callback) { + TRACE_EVENT0("drm", "GbmSurfaceless::OnSwapBuffersAsync"); window_->SchedulePageFlip(planes_, callback); planes_.clear(); } @@ -56,4 +60,24 @@ bool GbmSurfaceless::IsUniversalDisplayLinkDevice() { return planes_.empty() ? false : planes_[0].buffer->RequiresGlFinish(); } +void* /* EGLConfig */ GbmSurfaceless::GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) { + EGLint config_attribs[] = {EGL_BUFFER_SIZE, + 32, + EGL_ALPHA_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_RED_SIZE, + 8, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, + EGL_DONT_CARE, + EGL_NONE}; + return ChooseEGLConfig(egl, config_attribs); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h index 91930020d87..306ef11368c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h @@ -40,6 +40,8 @@ class GbmSurfaceless : public SurfaceOzoneEGL { void OnSwapBuffersAsync(const SwapCompletionCallback& callback) override; scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override; bool IsUniversalDisplayLinkDevice() override; + void* /* EGLConfig */ GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) override; protected: scoped_ptr<DrmWindowProxy> window_; @@ -48,6 +50,7 @@ class GbmSurfaceless : public SurfaceOzoneEGL { std::vector<OverlayPlane> planes_; + private: DISALLOW_COPY_AND_ASSIGN(GbmSurfaceless); }; 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 220745235e9..8ec577d1f1d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h @@ -18,7 +18,6 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "ui/gfx/swap_result.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" #include "ui/ozone/platform/drm/gpu/overlay_plane.h" @@ -86,7 +85,7 @@ class DrmDevice; // only a subset of connectors can be active independently, showing different // framebuffers. Though, in this case, it would be possible to have all // connectors active if some use the same CRTC to mirror the display. -class OZONE_EXPORT HardwareDisplayController { +class HardwareDisplayController { typedef base::Callback<void(gfx::SwapResult)> PageFlipCallback; public: 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 798d2f36c5c..729df45bd61 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h @@ -11,7 +11,6 @@ #include <vector> #include "base/macros.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" namespace gfx { @@ -22,7 +21,7 @@ namespace ui { class DrmDevice; -class OZONE_EXPORT HardwareDisplayPlane { +class HardwareDisplayPlane { public: enum Type { kDummy, kPrimary, kOverlay, kCursor }; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc index c4cc7e05e87..37ce65322b7 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc @@ -36,6 +36,9 @@ HardwareDisplayPlaneList::PageFlipInfo::PageFlipInfo(uint32_t crtc_id, : crtc_id(crtc_id), framebuffer(framebuffer), crtc(crtc) { } +HardwareDisplayPlaneList::PageFlipInfo::PageFlipInfo( + const PageFlipInfo& other) = default; + HardwareDisplayPlaneList::PageFlipInfo::~PageFlipInfo() { } @@ -231,7 +234,6 @@ bool HardwareDisplayPlaneManager::AssignOverlayPlanes( size_t plane_idx = 0; HardwareDisplayPlane* primary_plane = nullptr; gfx::Rect primary_display_bounds; - gfx::Rect primary_src_rect; uint32_t primary_format; for (const auto& plane : overlay_list) { HardwareDisplayPlane* hw_plane = @@ -269,15 +271,13 @@ bool HardwareDisplayPlaneManager::AssignOverlayPlanes( // TODO(kalyank): Check if we can move this optimization to // DrmOverlayCandidatesHost. if (!needs_blending && primary_format == fourcc_format && - primary_display_bounds == plane.display_bounds && - fixed_point_rect == primary_src_rect) { + primary_display_bounds == plane.display_bounds) { ResetCurrentPlaneList(plane_list); hw_plane = primary_plane; } } else { primary_plane = hw_plane; primary_display_bounds = plane.display_bounds; - primary_src_rect = fixed_point_rect; primary_format = fourcc_format; } 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 e9b04ec06b0..3efdd8b0304 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 @@ -12,7 +12,6 @@ #include "base/macros.h" #include "base/memory/scoped_vector.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" #include "ui/ozone/platform/drm/gpu/overlay_plane.h" @@ -28,7 +27,7 @@ class DrmDevice; // This contains the list of planes controlled by one HDC on a given DRM fd. // It is owned by the HDC and filled by the CrtcController. -struct OZONE_EXPORT HardwareDisplayPlaneList { +struct HardwareDisplayPlaneList { HardwareDisplayPlaneList(); ~HardwareDisplayPlaneList(); @@ -39,6 +38,7 @@ struct OZONE_EXPORT HardwareDisplayPlaneList { struct PageFlipInfo { PageFlipInfo(uint32_t crtc_id, uint32_t framebuffer, CrtcController* crtc); + PageFlipInfo(const PageFlipInfo& other); ~PageFlipInfo(); uint32_t crtc_id; @@ -67,7 +67,7 @@ struct OZONE_EXPORT HardwareDisplayPlaneList { #endif // defined(USE_DRM_ATOMIC) }; -class OZONE_EXPORT HardwareDisplayPlaneManager { +class HardwareDisplayPlaneManager { public: HardwareDisplayPlaneManager(); virtual ~HardwareDisplayPlaneManager(); 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 3fc911c50d1..d9512eb8727 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 @@ -8,13 +8,11 @@ #include <stdint.h> #include "base/macros.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" namespace ui { -class OZONE_EXPORT HardwareDisplayPlaneManagerAtomic - : public HardwareDisplayPlaneManager { +class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { public: HardwareDisplayPlaneManagerAtomic(); ~HardwareDisplayPlaneManagerAtomic() override; 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 c716187e111..56c55766e0f 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 @@ -8,13 +8,11 @@ #include <stdint.h> #include "base/macros.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" namespace ui { -class OZONE_EXPORT HardwareDisplayPlaneManagerLegacy - : public HardwareDisplayPlaneManager { +class HardwareDisplayPlaneManagerLegacy : public HardwareDisplayPlaneManager { public: HardwareDisplayPlaneManagerLegacy(); ~HardwareDisplayPlaneManagerLegacy() override; diff --git a/chromium/ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.cc b/chromium/ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.cc new file mode 100644 index 00000000000..fd909c29a8b --- /dev/null +++ b/chromium/ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.cc @@ -0,0 +1,11 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h" + +namespace ui { + +InterThreadMessagingProxy::~InterThreadMessagingProxy() {} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h b/chromium/ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h new file mode 100644 index 00000000000..b0fe234c415 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h @@ -0,0 +1,22 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_DRM_GPU_INTER_THREAD_MESSAGING_PROXY_H_ +#define UI_OZONE_PLATFORM_DRM_GPU_INTER_THREAD_MESSAGING_PROXY_H_ + +#include "base/macros.h" + +namespace ui { + +class DrmThread; + +class InterThreadMessagingProxy { + public: + virtual ~InterThreadMessagingProxy(); + virtual void SetDrmThread(DrmThread* thread) = 0; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_DRM_GPU_INTER_THREAD_MESSAGING_PROXY_H_ 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 8dd6af0a7e8..9ab4e237ab2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc @@ -185,8 +185,7 @@ bool MockDrmDevice::CreateDumbBuffer(const SkImageInfo& info, *handle = allocate_buffer_count_++; *stride = info.minRowBytes(); void* pixels = new char[info.getSafeSize(*stride)]; - buffers_.push_back( - skia::AdoptRef(SkSurface::NewRasterDirect(info, pixels, *stride))); + buffers_.push_back(SkSurface::MakeRasterDirect(info, pixels, *stride)); buffers_[*handle]->getCanvas()->clear(SK_ColorBLACK); return true; @@ -196,7 +195,7 @@ bool MockDrmDevice::DestroyDumbBuffer(uint32_t handle) { if (handle >= buffers_.size() || !buffers_[handle]) return false; - buffers_[handle].clear(); + buffers_[handle].reset(); return true; } @@ -228,6 +227,14 @@ bool MockDrmDevice::SetGammaRamp(uint32_t crtc_id, return true; } +bool MockDrmDevice::SetColorCorrection( + uint32_t crtc_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + return true; +} + bool MockDrmDevice::SetCapability(uint64_t capability, uint64_t value) { return false; } diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h index 48cd37e1d18..13506eee1d3 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h @@ -50,9 +50,7 @@ class MockDrmDevice : public DrmDevice { uint32_t current_framebuffer() const { return current_framebuffer_; } - const std::vector<skia::RefPtr<SkSurface>> buffers() const { - return buffers_; - } + const std::vector<sk_sp<SkSurface>> buffers() const { return buffers_; } uint32_t get_cursor_handle_for_crtc(uint32_t crtc) const { const auto it = crtc_cursor_map_.find(crtc); @@ -113,6 +111,10 @@ class MockDrmDevice : public DrmDevice { const PageFlipCallback& callback) override; bool SetGammaRamp(uint32_t crtc_id, const std::vector<GammaRampRGBEntry>& lut) override; + bool SetColorCorrection(uint32_t crtc_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) override; bool SetCapability(uint64_t capability, uint64_t value) override; private: @@ -137,7 +139,7 @@ class MockDrmDevice : public DrmDevice { uint32_t current_framebuffer_; - std::vector<skia::RefPtr<SkSurface>> buffers_; + std::vector<sk_sp<SkSurface>> buffers_; std::map<uint32_t, uint32_t> crtc_cursor_map_; diff --git a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc index c883eb00aa3..d7016701cd2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc +++ b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc @@ -27,6 +27,8 @@ OverlayPlane::OverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer, display_bounds(display_bounds), crop_rect(crop_rect) {} +OverlayPlane::OverlayPlane(const OverlayPlane& other) = default; + OverlayPlane::~OverlayPlane() { } diff --git a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h index 1666e07d870..1225049a767 100644 --- a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h +++ b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h @@ -12,7 +12,6 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/overlay_transform.h" -#include "ui/ozone/ozone_export.h" namespace ui { @@ -21,7 +20,7 @@ class ScanoutBuffer; struct OverlayPlane; typedef std::vector<OverlayPlane> OverlayPlaneList; -struct OZONE_EXPORT OverlayPlane { +struct OverlayPlane { // Simpler constructor for the primary plane. explicit OverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer); @@ -30,6 +29,7 @@ struct OZONE_EXPORT OverlayPlane { gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, const gfx::RectF& crop_rect); + OverlayPlane(const OverlayPlane& other); // This represents a callback function which can handle post processing // operations like scaling, format conversion etc of the buffer bound to this diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h index aae384b63f6..59f81d2cdb0 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h @@ -12,7 +12,6 @@ #include "base/memory/scoped_vector.h" #include "base/observer_list.h" #include "ui/gfx/native_widget_types.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h" typedef struct _drmModeModeInfo drmModeModeInfo; @@ -30,7 +29,7 @@ class DrmWindow; class ScanoutBufferGenerator; // Responsible for keeping track of active displays and configuring them. -class OZONE_EXPORT ScreenManager { +class ScreenManager { public: ScreenManager(ScanoutBufferGenerator* surface_generator); virtual ~ScreenManager(); diff --git a/chromium/ui/ozone/platform/drm/host/channel_observer.h b/chromium/ui/ozone/platform/drm/host/channel_observer.h deleted file mode 100644 index ffbfa048a89..00000000000 --- a/chromium/ui/ozone/platform/drm/host/channel_observer.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_OZONE_PLATFORM_DRM_HOST_CHANNEL_OBSERVER_H_ -#define UI_OZONE_PLATFORM_DRM_HOST_CHANNEL_OBSERVER_H_ - -namespace ui { - -// Observes the channel state. -class ChannelObserver { - public: - virtual ~ChannelObserver() {} - - virtual void OnChannelEstablished() = 0; - virtual void OnChannelDestroyed() = 0; -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_DRM_HOST_CHANNEL_OBSERVER_H_ diff --git a/chromium/ui/ozone/platform/drm/host/drm_cursor.cc b/chromium/ui/ozone/platform/drm/host/drm_cursor.cc index 31c5b9fb877..b3acdc477e8 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_cursor.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_cursor.cc @@ -1,16 +1,12 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/ozone/platform/drm/host/drm_cursor.h" -#include "base/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" -#include "ui/gfx/geometry/point.h" +#include "base/trace_event/trace_event.h" #include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/geometry/point_f.h" -#include "ui/ozone/common/gpu/ozone_gpu_messages.h" +#include "ui/ozone/platform/drm/host/drm_window_host.h" #include "ui/ozone/platform/drm/host/drm_window_host_manager.h" #if defined(OS_CHROMEOS) @@ -19,10 +15,50 @@ namespace ui { +namespace { + +class NullProxy : public DrmCursorProxy { + public: + NullProxy() {} + ~NullProxy() override {} + + 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 {} + + private: + DISALLOW_COPY_AND_ASSIGN(NullProxy); +}; + +} // namespace + DrmCursor::DrmCursor(DrmWindowHostManager* window_manager) - : ipc_(&lock_), core_(new DrmCursorCore(&ipc_, window_manager)) {} + : window_(gfx::kNullAcceleratedWidget), + window_manager_(window_manager), + proxy_(new NullProxy()) {} + +DrmCursor::~DrmCursor() {} -DrmCursor::~DrmCursor() { +void DrmCursor::SetDrmCursorProxy(DrmCursorProxy* proxy) { + TRACE_EVENT0("drmcursor", "DrmCursor::SetDrmCursorProxy"); + DCHECK(thread_checker_.CalledOnValidThread()); + base::AutoLock lock(lock_); + proxy_.reset(proxy); +} + +void DrmCursor::ResetDrmCursorProxy() { + TRACE_EVENT0("drmcursor", "DrmCursor::ResetDrmCursorProxy"); + DCHECK(thread_checker_.CalledOnValidThread()); + + NullProxy* np = new NullProxy(); + base::AutoLock lock(lock_); + proxy_.reset(np); +} + +gfx::Point DrmCursor::GetBitmapLocationLocked() { + return gfx::ToFlooredPoint(location_) - bitmap_->hotspot().OffsetFromOrigin(); } void DrmCursor::SetCursor(gfx::AcceleratedWidget window, @@ -35,7 +71,13 @@ void DrmCursor::SetCursor(gfx::AcceleratedWidget window, BitmapCursorFactoryOzone::GetBitmapCursor(platform_cursor); base::AutoLock lock(lock_); - core_->SetCursor(window, bitmap); + + if (window_ != window || bitmap_ == bitmap) + return; + + bitmap_ = bitmap; + + SendCursorShowLocked(); } void DrmCursor::OnWindowAdded(gfx::AcceleratedWidget window, @@ -45,14 +87,37 @@ void DrmCursor::OnWindowAdded(gfx::AcceleratedWidget window, DCHECK(thread_checker_.CalledOnValidThread()); base::AutoLock lock(lock_); - core_->OnWindowAdded(window, bounds_in_screen, cursor_confined_bounds); + if (window_ == gfx::kNullAcceleratedWidget) { + // First window added & cursor is not placed. Place it. + window_ = window; + display_bounds_in_screen_ = bounds_in_screen; + confined_bounds_ = cursor_confined_bounds; + SetCursorLocationLocked(gfx::PointF(cursor_confined_bounds.CenterPoint())); + } } void DrmCursor::OnWindowRemoved(gfx::AcceleratedWidget window) { TRACE_EVENT0("drmcursor", "DrmCursor::OnWindowRemoved"); DCHECK(thread_checker_.CalledOnValidThread()); base::AutoLock lock(lock_); - core_->OnWindowRemoved(window); + + if (window_ == window) { + // Try to find a new location for the cursor. + DrmWindowHost* dest_window = window_manager_->GetPrimaryWindow(); + + if (dest_window) { + window_ = dest_window->GetAcceleratedWidget(); + display_bounds_in_screen_ = dest_window->GetBounds(); + confined_bounds_ = dest_window->GetCursorConfinedBounds(); + SetCursorLocationLocked(gfx::PointF(confined_bounds_.CenterPoint())); + SendCursorShowLocked(); + } else { + window_ = gfx::kNullAcceleratedWidget; + display_bounds_in_screen_ = gfx::Rect(); + confined_bounds_ = gfx::Rect(); + location_ = gfx::PointF(); + } + } } void DrmCursor::CommitBoundsChange( @@ -62,8 +127,13 @@ void DrmCursor::CommitBoundsChange( TRACE_EVENT0("drmcursor", "DrmCursor::CommitBoundsChange"); DCHECK(thread_checker_.CalledOnValidThread()); base::AutoLock lock(lock_); - core_->CommitBoundsChange(window, new_display_bounds_in_screen, - new_confined_bounds); + + if (window_ == window) { + display_bounds_in_screen_ = new_display_bounds_in_screen; + confined_bounds_ = new_confined_bounds; + SetCursorLocationLocked(location_); + SendCursorShowLocked(); + } } void DrmCursor::MoveCursorTo(gfx::AcceleratedWidget window, @@ -71,96 +141,118 @@ void DrmCursor::MoveCursorTo(gfx::AcceleratedWidget window, TRACE_EVENT0("drmcursor", "DrmCursor::MoveCursorTo (window)"); DCHECK(thread_checker_.CalledOnValidThread()); base::AutoLock lock(lock_); - core_->MoveCursorTo(window, location); + gfx::AcceleratedWidget old_window = window_; + + if (window != old_window) { + // When moving between displays, hide the cursor on the old display + // prior to showing it on the new display. + if (old_window != gfx::kNullAcceleratedWidget) + SendCursorHideLocked(); + + // TODO(rjk): pass this in? + DrmWindowHost* drm_window_host = window_manager_->GetWindow(window); + display_bounds_in_screen_ = drm_window_host->GetBounds(); + confined_bounds_ = drm_window_host->GetCursorConfinedBounds(); + window_ = window; + } + + SetCursorLocationLocked(location); + if (window != old_window) + SendCursorShowLocked(); + else + SendCursorMoveLocked(); } void DrmCursor::MoveCursorTo(const gfx::PointF& screen_location) { TRACE_EVENT0("drmcursor", "DrmCursor::MoveCursorTo"); DCHECK(thread_checker_.CalledOnValidThread()); base::AutoLock lock(lock_); - core_->MoveCursorTo(screen_location); + + // TODO(spang): Moving between windows doesn't work here, but + // is not needed for current uses. + SetCursorLocationLocked(screen_location - + display_bounds_in_screen_.OffsetFromOrigin()); + + SendCursorMoveLocked(); } void DrmCursor::MoveCursor(const gfx::Vector2dF& delta) { TRACE_EVENT0("drmcursor", "DrmCursor::MoveCursor"); base::AutoLock lock(lock_); - core_->MoveCursor(delta); + + if (window_ == gfx::kNullAcceleratedWidget) + return; + + gfx::Point location; +#if defined(OS_CHROMEOS) + gfx::Vector2dF transformed_delta = delta; + ui::CursorController::GetInstance()->ApplyCursorConfigForWindow( + window_, &transformed_delta); + SetCursorLocationLocked(location_ + transformed_delta); +#else + SetCursorLocationLocked(location_ + delta); +#endif + SendCursorMoveLocked(); } bool DrmCursor::IsCursorVisible() { base::AutoLock lock(lock_); - return core_->IsCursorVisible(); + return bitmap_; } gfx::PointF DrmCursor::GetLocation() { base::AutoLock lock(lock_); - return core_->GetLocation(); + return location_ + display_bounds_in_screen_.OffsetFromOrigin(); } gfx::Rect DrmCursor::GetCursorConfinedBounds() { base::AutoLock lock(lock_); - return core_->GetCursorConfinedBounds(); + return confined_bounds_ + display_bounds_in_screen_.OffsetFromOrigin(); } -void DrmCursor::OnChannelEstablished( - int host_id, - scoped_refptr<base::SingleThreadTaskRunner> send_runner, - const base::Callback<void(IPC::Message*)>& send_callback) { - TRACE_EVENT0("drmcursor", "DrmCursor::OnChannelEstablished"); - DCHECK(thread_checker_.CalledOnValidThread()); - base::AutoLock lock(lock_); - ipc_.host_id = host_id; - ipc_.send_runner = send_runner; - ipc_.send_callback = send_callback; - // Initial set for this GPU process will happen after the window - // initializes, in CommitBoundsChange(). -} +void DrmCursor::SetCursorLocationLocked(const gfx::PointF& location) { + gfx::PointF clamped_location = location; + clamped_location.SetToMax(gfx::PointF(confined_bounds_.origin())); + // Right and bottom edges are exclusive. + clamped_location.SetToMin( + gfx::PointF(confined_bounds_.right() - 1, confined_bounds_.bottom() - 1)); -void DrmCursor::OnChannelDestroyed(int host_id) { - DCHECK(thread_checker_.CalledOnValidThread()); - base::AutoLock lock(lock_); - if (ipc_.host_id == host_id) { - ipc_.host_id = -1; - ipc_.send_runner = NULL; - ipc_.send_callback.Reset(); - } + location_ = clamped_location; } -bool DrmCursor::OnMessageReceived(const IPC::Message& message) { - return false; +void DrmCursor::SendCursorShowLocked() { + if (!bitmap_) { + SendCursorHideLocked(); + return; + } + CursorSetLockTested(window_, bitmap_->bitmaps(), GetBitmapLocationLocked(), + bitmap_->frame_delay_ms()); } -bool DrmCursor::CursorIPC::IsConnectedLocked() { - return !send_callback.is_null(); +void DrmCursor::SendCursorHideLocked() { + CursorSetLockTested(window_, std::vector<SkBitmap>(), gfx::Point(), 0); } -void DrmCursor::CursorIPC::CursorSet(gfx::AcceleratedWidget window, - const std::vector<SkBitmap>& bitmaps, - gfx::Point point, - int frame_delay_ms) { - SendLocked(new OzoneGpuMsg_CursorSet(window, bitmaps, point, frame_delay_ms)); +void DrmCursor::SendCursorMoveLocked() { + if (!bitmap_) + return; + MoveLockTested(window_, GetBitmapLocationLocked()); } -void DrmCursor::CursorIPC::Move(gfx::AcceleratedWidget window, - gfx::Point point) { - SendLocked(new OzoneGpuMsg_CursorMove(window, point)); +// Lock-testing helpers. +void DrmCursor::CursorSetLockTested(gfx::AcceleratedWidget window, + const std::vector<SkBitmap>& bitmaps, + const gfx::Point& point, + int frame_delay_ms) { + lock_.AssertAcquired(); + proxy_->CursorSet(window, bitmaps, point, frame_delay_ms); } -void DrmCursor::CursorIPC::SendLocked(IPC::Message* message) { - lock->AssertAcquired(); - - if (IsConnectedLocked() && - send_runner->PostTask(FROM_HERE, base::Bind(send_callback, message))) - return; - - // Drop disconnected updates. DrmWindowHost will call - // CommitBoundsChange() when we connect to initialize the cursor - // location. - delete message; +void DrmCursor::MoveLockTested(gfx::AcceleratedWidget window, + const gfx::Point& point) { + lock_.AssertAcquired(); + proxy_->Move(window, point); } -DrmCursor::CursorIPC::CursorIPC(base::Lock* lock) : host_id(-1), lock(lock) {} - -DrmCursor::CursorIPC::~CursorIPC() {} } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/host/drm_cursor.h b/chromium/ui/ozone/platform/drm/host/drm_cursor.h index 2766365f091..70b18f37e9e 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_cursor.h +++ b/chromium/ui/ozone/platform/drm/host/drm_cursor.h @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// 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. @@ -6,36 +6,45 @@ #define UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_ #include <memory> +#include <vector> -#include "base/callback.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" #include "base/synchronization/lock.h" #include "base/threading/thread_checker.h" -#include "ui/base/cursor/cursor.h" +#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/events/ozone/evdev/cursor_delegate_evdev.h" -#include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/rect.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/ozone/platform/drm/host/drm_cursor_core.h" -#include "ui/ozone/public/gpu_platform_support_host.h" - -namespace gfx { -class PointF; -class Vector2dF; -class Rect; -} namespace ui { -class BitmapCursorFactoryOzone; +class BitmapCursorOzone; class DrmWindowHostManager; +// DrmCursor manages all cursor state but is dependent on an injected +// proxy for how it communicates state changes to other threads or +// processes. The proxy implementation must satisfy DrmCursorProxy. +class DrmCursorProxy { + public: + virtual ~DrmCursorProxy() {} + + // Sets the cursor |bitmaps| on |window| at |point| with |frame_delay_ms|. + virtual void CursorSet(gfx::AcceleratedWidget window, + const std::vector<SkBitmap>& bitmaps, + const gfx::Point& point, + int frame_delay_ms) = 0; + // Moves the cursor in |window| to |point| + virtual void Move(gfx::AcceleratedWidget window, const gfx::Point& point) = 0; +}; + +// DrmCursor manages all cursor state and semantics. class DrmCursor : public CursorDelegateEvdev { public: explicit DrmCursor(DrmWindowHostManager* window_manager); ~DrmCursor() override; + // Sets or resets the DrmProxy |proxy|. If |proxy| is set, the DrmCursor uses + // it to communicate to the GPU process or thread. + void SetDrmCursorProxy(DrmCursorProxy* proxy); + void ResetDrmCursorProxy(); + // Change the cursor over the specifed window. void SetCursor(gfx::AcceleratedWidget window, PlatformCursor platform_cursor); @@ -59,39 +68,18 @@ class DrmCursor : public CursorDelegateEvdev { gfx::PointF GetLocation() override; gfx::Rect GetCursorConfinedBounds() override; - // IPC. - void OnChannelEstablished( - int host_id, - scoped_refptr<base::SingleThreadTaskRunner> send_runner, - const base::Callback<void(IPC::Message*)>& sender); - void OnChannelDestroyed(int host_id); - bool OnMessageReceived(const IPC::Message& message); - private: - // An IPC-based implementation of the DrmCursorProxy that ships - // messages to the GPU process. - class CursorIPC : public DrmCursorProxy { - public: - explicit CursorIPC(base::Lock* lock); - ~CursorIPC(); - - bool IsConnectedLocked(); - void SendLocked(IPC::Message* message); - - // DrmCursorProxy implementation. - void CursorSet(gfx::AcceleratedWidget window, - const std::vector<SkBitmap>& bitmaps, - gfx::Point point, - int frame_delay_ms) override; - void Move(gfx::AcceleratedWidget window, gfx::Point point) override; - - int host_id; - - // Callback for IPC updates. - base::Callback<void(IPC::Message*)> send_callback; - scoped_refptr<base::SingleThreadTaskRunner> send_runner; - base::Lock* lock; - }; + void SetCursorLocationLocked(const gfx::PointF& location); + void SendCursorShowLocked(); + void SendCursorHideLocked(); + void SendCursorMoveLocked(); + + // Lock-testing helpers. + void CursorSetLockTested(gfx::AcceleratedWidget window, + const std::vector<SkBitmap>& bitmaps, + const gfx::Point& point, + int frame_delay_ms); + void MoveLockTested(gfx::AcceleratedWidget window, const gfx::Point& point); // The mutex synchronizing this object. base::Lock lock_; @@ -99,8 +87,27 @@ class DrmCursor : public CursorDelegateEvdev { // Enforce our threading constraints. base::ThreadChecker thread_checker_; - CursorIPC ipc_; - std::unique_ptr<DrmCursorCore> core_; + // The location of the bitmap (the cursor location is the hotspot location). + gfx::Point GetBitmapLocationLocked(); + + // The current cursor bitmap (immutable). + scoped_refptr<BitmapCursorOzone> bitmap_; + + // The window under the cursor. + gfx::AcceleratedWidget window_; + + // The location of the cursor within the window. + gfx::PointF location_; + + // The bounds of the display under the cursor. + gfx::Rect display_bounds_in_screen_; + + // The bounds that the cursor is confined to in |window|. + gfx::Rect confined_bounds_; + + DrmWindowHostManager* window_manager_; // Not owned. + + std::unique_ptr<DrmCursorProxy> proxy_; DISALLOW_COPY_AND_ASSIGN(DrmCursor); }; diff --git a/chromium/ui/ozone/platform/drm/host/drm_cursor_core.cc b/chromium/ui/ozone/platform/drm/host/drm_cursor_core.cc deleted file mode 100644 index 86b92d494a8..00000000000 --- a/chromium/ui/ozone/platform/drm/host/drm_cursor_core.cc +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/ozone/platform/drm/host/drm_cursor_core.h" - -#include "ui/events/ozone/chromeos/cursor_controller.h" -#include "ui/gfx/geometry/point_conversions.h" -#include "ui/ozone/platform/drm/host/drm_window_host.h" -#include "ui/ozone/platform/drm/host/drm_window_host_manager.h" - -namespace ui { - -DrmCursorCore::DrmCursorCore(DrmCursorProxy* proxy, - DrmWindowHostManager* window_manager) - : window_(gfx::kNullAcceleratedWidget), - window_manager_(window_manager), - proxy_(proxy) {} - -DrmCursorCore::~DrmCursorCore() {} - -gfx::Point DrmCursorCore::GetBitmapLocationLocked() { - return gfx::ToFlooredPoint(location_) - bitmap_->hotspot().OffsetFromOrigin(); -} - -void DrmCursorCore::SetCursor(gfx::AcceleratedWidget window, - scoped_refptr<BitmapCursorOzone> bitmap) { - if (window_ != window || bitmap_ == bitmap) - return; - - bitmap_ = bitmap; - - SendCursorShow(); -} - -void DrmCursorCore::OnWindowAdded(gfx::AcceleratedWidget window, - const gfx::Rect& bounds_in_screen, - const gfx::Rect& cursor_confined_bounds) { - if (window_ == gfx::kNullAcceleratedWidget) { - // First window added & cursor is not placed. Place it. - window_ = window; - display_bounds_in_screen_ = bounds_in_screen; - confined_bounds_ = cursor_confined_bounds; - SetCursorLocation(gfx::PointF(cursor_confined_bounds.CenterPoint())); - } -} - -void DrmCursorCore::OnWindowRemoved(gfx::AcceleratedWidget window) { - if (window_ == window) { - // Try to find a new location for the cursor. - DrmWindowHost* dest_window = window_manager_->GetPrimaryWindow(); - - if (dest_window) { - window_ = dest_window->GetAcceleratedWidget(); - display_bounds_in_screen_ = dest_window->GetBounds(); - confined_bounds_ = dest_window->GetCursorConfinedBounds(); - SetCursorLocation(gfx::PointF(confined_bounds_.CenterPoint())); - SendCursorShow(); - } else { - window_ = gfx::kNullAcceleratedWidget; - display_bounds_in_screen_ = gfx::Rect(); - confined_bounds_ = gfx::Rect(); - location_ = gfx::PointF(); - } - } -} - -void DrmCursorCore::CommitBoundsChange( - gfx::AcceleratedWidget window, - const gfx::Rect& new_display_bounds_in_screen, - const gfx::Rect& new_confined_bounds) { - if (window_ == window) { - display_bounds_in_screen_ = new_display_bounds_in_screen; - confined_bounds_ = new_confined_bounds; - SetCursorLocation(location_); - SendCursorShow(); - } -} - -void DrmCursorCore::MoveCursorTo(gfx::AcceleratedWidget window, - const gfx::PointF& location) { - gfx::AcceleratedWidget old_window = window_; - - if (window != old_window) { - // When moving between displays, hide the cursor on the old display - // prior to showing it on the new display. - if (old_window != gfx::kNullAcceleratedWidget) - SendCursorHide(); - - // TODO(rjk): pass this in? - DrmWindowHost* drm_window_host = window_manager_->GetWindow(window); - display_bounds_in_screen_ = drm_window_host->GetBounds(); - confined_bounds_ = drm_window_host->GetCursorConfinedBounds(); - window_ = window; - } - - SetCursorLocation(location); - if (window != old_window) - SendCursorShow(); - else - SendCursorMove(); -} - -void DrmCursorCore::MoveCursorTo(const gfx::PointF& screen_location) { - // TODO(spang): Moving between windows doesn't work here, but - // is not needed for current uses. - SetCursorLocation(screen_location - - display_bounds_in_screen_.OffsetFromOrigin()); - - SendCursorMove(); -} - -void DrmCursorCore::MoveCursor(const gfx::Vector2dF& delta) { - if (window_ == gfx::kNullAcceleratedWidget) - return; - - gfx::Point location; -#if defined(OS_CHROMEOS) - gfx::Vector2dF transformed_delta = delta; - ui::CursorController::GetInstance()->ApplyCursorConfigForWindow( - window_, &transformed_delta); - SetCursorLocation(location_ + transformed_delta); -#else - SetCursorLocation(location_ + delta); -#endif - SendCursorMove(); -} - -bool DrmCursorCore::IsCursorVisible() { - return bitmap_; -} - -gfx::PointF DrmCursorCore::GetLocation() { - return location_ + display_bounds_in_screen_.OffsetFromOrigin(); -} - -gfx::Rect DrmCursorCore::GetCursorConfinedBounds() { - return confined_bounds_ + display_bounds_in_screen_.OffsetFromOrigin(); -} - -void DrmCursorCore::SetCursorLocation(const gfx::PointF& location) { - gfx::PointF clamped_location = location; - clamped_location.SetToMax(gfx::PointF(confined_bounds_.origin())); - // Right and bottom edges are exclusive. - clamped_location.SetToMin( - gfx::PointF(confined_bounds_.right() - 1, confined_bounds_.bottom() - 1)); - - location_ = clamped_location; -} - -void DrmCursorCore::SendCursorShow() { - if (!bitmap_) { - SendCursorHide(); - return; - } - proxy_->CursorSet(window_, bitmap_->bitmaps(), GetBitmapLocationLocked(), - bitmap_->frame_delay_ms()); -} - -void DrmCursorCore::SendCursorHide() { - proxy_->CursorSet(window_, std::vector<SkBitmap>(), gfx::Point(), 0); -} - -void DrmCursorCore::SendCursorMove() { - if (!bitmap_) - return; - - proxy_->Move(window_, GetBitmapLocationLocked()); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/host/drm_cursor_core.h b/chromium/ui/ozone/platform/drm/host/drm_cursor_core.h deleted file mode 100644 index 3dc7bce1edb..00000000000 --- a/chromium/ui/ozone/platform/drm/host/drm_cursor_core.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_CORE_H_ -#define UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_CORE_H_ - -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" -#include "ui/gfx/geometry/point_f.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/native_widget_types.h" - -namespace ui { -class BitmapCursorOzone; -class DrmWindowHostManager; - -// DrmCursorCore manages all cursor state but is dependent on an injected -// proxy for how it communicates state changes to other threads or -// processes. The proxy implementation must satisfy DrmCursorProxy. -class DrmCursorProxy { - public: - // Sets the cursor |bitmaps| on |window| at |point| with |frame_delay_ms|. - virtual void CursorSet(gfx::AcceleratedWidget window, - const std::vector<SkBitmap>& bitmaps, - gfx::Point point, - int frame_delay_ms) = 0; - // Moves the cursor in |window| to |point| - virtual void Move(gfx::AcceleratedWidget window, gfx::Point point) = 0; -}; - -// DrmCursorCore manages all cursor state and semantics. -class DrmCursorCore { - public: - DrmCursorCore(DrmCursorProxy* proxy, DrmWindowHostManager* window_manager); - ~DrmCursorCore(); - - // Change the cursor over the specifed window. - void SetCursor(gfx::AcceleratedWidget window, - scoped_refptr<BitmapCursorOzone> platform_cursor); - - // Handle window lifecycle. - void OnWindowAdded(gfx::AcceleratedWidget window, - const gfx::Rect& bounds_in_screen, - const gfx::Rect& cursor_confined_bounds); - void OnWindowRemoved(gfx::AcceleratedWidget window); - - // Handle window bounds changes. - void CommitBoundsChange(gfx::AcceleratedWidget window, - const gfx::Rect& new_display_bounds_in_screen, - const gfx::Rect& new_confined_bounds); - - // CursorDelegateEvdev: - void MoveCursorTo(gfx::AcceleratedWidget window, const gfx::PointF& location); - void MoveCursorTo(const gfx::PointF& screen_location); - void MoveCursor(const gfx::Vector2dF& delta); - bool IsCursorVisible(); - gfx::PointF GetLocation(); - gfx::Rect GetCursorConfinedBounds(); - - private: - void SetCursorLocation(const gfx::PointF& location); - void SendCursorShow(); - void SendCursorHide(); - void SendCursorMove(); - - // The location of the bitmap (the cursor location is the hotspot location). - gfx::Point GetBitmapLocationLocked(); - - // The current cursor bitmap (immutable). - scoped_refptr<BitmapCursorOzone> bitmap_; - - // The window under the cursor. - gfx::AcceleratedWidget window_; - - // The location of the cursor within the window. - gfx::PointF location_; - - // The bounds of the display under the cursor. - gfx::Rect display_bounds_in_screen_; - - // The bounds that the cursor is confined to in |window|. - gfx::Rect confined_bounds_; - - DrmWindowHostManager* window_manager_; // Not owned. - - // Lifetime managed by the caller. - DrmCursorProxy* proxy_; - - DISALLOW_COPY_AND_ASSIGN(DrmCursorCore); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_ 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 9491e4ef2c4..32d36e65b03 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc @@ -4,25 +4,25 @@ #include "ui/ozone/platform/drm/host/drm_display_host.h" +#include "base/location.h" #include "base/thread_task_runner_handle.h" #include "ui/ozone/common/display_snapshot_proxy.h" #include "ui/ozone/common/display_util.h" -#include "ui/ozone/common/gpu/ozone_gpu_messages.h" -#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h" +#include "ui/ozone/platform/drm/host/gpu_thread_adapter.h" namespace ui { -DrmDisplayHost::DrmDisplayHost(DrmGpuPlatformSupportHost* sender, +DrmDisplayHost::DrmDisplayHost(GpuThreadAdapter* sender, const DisplaySnapshot_Params& params, bool is_dummy) : sender_(sender), snapshot_(new DisplaySnapshotProxy(params)), is_dummy_(is_dummy) { - sender_->AddChannelObserver(this); + sender_->AddGpuThreadObserver(this); } DrmDisplayHost::~DrmDisplayHost() { - sender_->RemoveChannelObserver(this); + sender_->RemoveGpuThreadObserver(this); ClearCallbacks(); } @@ -42,11 +42,10 @@ void DrmDisplayHost::Configure(const DisplayMode* mode, configure_callback_ = callback; bool status = false; if (mode) { - status = sender_->Send(new OzoneGpuMsg_ConfigureNativeDisplay( - snapshot_->display_id(), GetDisplayModeParams(*mode), origin)); + status = sender_->GpuConfigureNativeDisplay( + snapshot_->display_id(), GetDisplayModeParams(*mode), origin); } else { - status = sender_->Send( - new OzoneGpuMsg_DisableNativeDisplay(snapshot_->display_id())); + status = sender_->GpuDisableNativeDisplay(snapshot_->display_id()); } if (!status) @@ -67,7 +66,7 @@ void DrmDisplayHost::OnDisplayConfigured(bool status) { void DrmDisplayHost::GetHDCPState(const GetHDCPStateCallback& callback) { get_hdcp_callback_ = callback; - if (!sender_->Send(new OzoneGpuMsg_GetHDCPState(snapshot_->display_id()))) + if (!sender_->GpuGetHDCPState(snapshot_->display_id())) OnHDCPStateReceived(false, HDCP_STATE_UNDESIRED); } @@ -86,8 +85,7 @@ void DrmDisplayHost::OnHDCPStateReceived(bool status, HDCPState state) { void DrmDisplayHost::SetHDCPState(HDCPState state, const SetHDCPStateCallback& callback) { set_hdcp_callback_ = callback; - if (!sender_->Send( - new OzoneGpuMsg_SetHDCPState(snapshot_->display_id(), state))) + if (!sender_->GpuSetHDCPState(snapshot_->display_id(), state)) OnHDCPStateUpdated(false); } @@ -103,11 +101,15 @@ void DrmDisplayHost::OnHDCPStateUpdated(bool status) { set_hdcp_callback_.Reset(); } -void DrmDisplayHost::SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut) { - sender_->Send(new OzoneGpuMsg_SetGammaRamp(snapshot_->display_id(), lut)); +void DrmDisplayHost::SetColorCorrection( + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + sender_->GpuSetColorCorrection(snapshot_->display_id(), degamma_lut, + gamma_lut, correction_matrix); } -void DrmDisplayHost::OnChannelEstablished() { +void DrmDisplayHost::OnGpuThreadReady() { is_dummy_ = false; // Note: These responses are done here since the OnChannelDestroyed() is @@ -115,8 +117,7 @@ void DrmDisplayHost::OnChannelEstablished() { ClearCallbacks(); } -void DrmDisplayHost::OnChannelDestroyed() { -} +void DrmDisplayHost::OnGpuThreadRetired() {} void DrmDisplayHost::ClearCallbacks() { if (!configure_callback_.is_null()) diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.h b/chromium/ui/ozone/platform/drm/host/drm_display_host.h index 5ea80fc63f7..32cc3523af1 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.h @@ -9,17 +9,17 @@ #include "base/memory/scoped_ptr.h" #include "ui/display/types/display_constants.h" #include "ui/display/types/native_display_delegate.h" -#include "ui/ozone/platform/drm/host/channel_observer.h" +#include "ui/ozone/platform/drm/host/gpu_thread_observer.h" namespace ui { struct DisplaySnapshot_Params; class DisplaySnapshot; -class DrmGpuPlatformSupportHost; +class GpuThreadAdapter; -class DrmDisplayHost : public ChannelObserver { +class DrmDisplayHost : public GpuThreadObserver { public: - DrmDisplayHost(DrmGpuPlatformSupportHost* sender, + DrmDisplayHost(GpuThreadAdapter* sender, const DisplaySnapshot_Params& params, bool is_dummy); ~DrmDisplayHost() override; @@ -32,7 +32,9 @@ class DrmDisplayHost : public ChannelObserver { const ConfigureCallback& callback); void GetHDCPState(const GetHDCPStateCallback& callback); void SetHDCPState(HDCPState state, const SetHDCPStateCallback& callback); - void SetGammaRamp(const std::vector<GammaRampRGBEntry>& lut); + void SetColorCorrection(const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix); // Called when the IPC from the GPU process arrives to answer the above // commands. @@ -40,15 +42,15 @@ class DrmDisplayHost : public ChannelObserver { void OnHDCPStateReceived(bool status, HDCPState state); void OnHDCPStateUpdated(bool status); - // ChannelObserver: - void OnChannelEstablished() override; - void OnChannelDestroyed() override; + // GpuThreadObserver: + void OnGpuThreadReady() override; + void OnGpuThreadRetired() override; private: // Calls all the callbacks with failure. void ClearCallbacks(); - DrmGpuPlatformSupportHost* sender_; // Not owned. + GpuThreadAdapter* sender_; // Not owned. scoped_ptr<DisplaySnapshot> snapshot_; 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 ba43cdc22e8..458c247ed2b 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 @@ -4,149 +4,461 @@ #include "ui/ozone/platform/drm/host/drm_display_host_manager.h" +#include <fcntl.h> +#include <stddef.h> +#include <xf86drm.h> +#include <utility> + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/strings/stringprintf.h" #include "base/thread_task_runner_handle.h" -#include "ui/ozone/common/gpu/ozone_gpu_messages.h" +#include "base/threading/thread_restrictions.h" +#include "base/threading/worker_pool.h" +#include "ui/display/types/display_snapshot.h" +#include "ui/events/ozone/device/device_event.h" +#include "ui/events/ozone/device/device_manager.h" +#include "ui/ozone/common/display_util.h" +#include "ui/ozone/platform/drm/common/drm_util.h" +#include "ui/ozone/platform/drm/host/drm_device_handle.h" #include "ui/ozone/platform/drm/host/drm_display_host.h" -#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h" #include "ui/ozone/platform/drm/host/drm_native_display_delegate.h" +#include "ui/ozone/platform/drm/host/gpu_thread_adapter.h" namespace ui { +namespace { + +typedef base::Callback<void(const base::FilePath&, + const base::FilePath&, + scoped_ptr<DrmDeviceHandle>)> + OnOpenDeviceReplyCallback; + +const char kDefaultGraphicsCardPattern[] = "/dev/dri/card%d"; + +const char* kDisplayActionString[] = { + "ADD", "REMOVE", "CHANGE", +}; + +// Find sysfs device path for the given device path. +base::FilePath MapDevPathToSysPath(const base::FilePath& device_path) { + // |device_path| looks something like /dev/dri/card0. We take the basename of + // that (card0) and append it to /sys/class/drm. /sys/class/drm/card0 is a + // symlink that points to something like + // /sys/devices/pci0000:00/0000:00:02.0/0000:05:00.0/drm/card0, which exposes + // some metadata about the attached device. + return base::MakeAbsoluteFilePath( + base::FilePath("/sys/class/drm").Append(device_path.BaseName())); +} + +void OpenDeviceOnWorkerThread( + const base::FilePath& device_path, + const scoped_refptr<base::TaskRunner>& reply_runner, + const OnOpenDeviceReplyCallback& callback) { + base::FilePath sys_path = MapDevPathToSysPath(device_path); + + scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle()); + handle->Initialize(device_path, sys_path); + reply_runner->PostTask(FROM_HERE, + base::Bind(callback, device_path, sys_path, + base::Passed(std::move(handle)))); +} + +base::FilePath GetPrimaryDisplayCardPath() { + struct drm_mode_card_res res; + for (int i = 0; /* end on first card# that does not exist */; i++) { + std::string card_path = base::StringPrintf(kDefaultGraphicsCardPattern, i); + + if (access(card_path.c_str(), F_OK) != 0) + break; + + int fd = open(card_path.c_str(), O_RDWR | O_CLOEXEC); + if (fd < 0) { + VPLOG(1) << "Failed to open '" << card_path << "'"; + continue; + } + + memset(&res, 0, sizeof(struct drm_mode_card_res)); + int ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res); + close(fd); + if (ret == 0 && res.count_crtcs > 0) { + return base::FilePath(card_path); + } + + VPLOG_IF(1, ret) << "Failed to get DRM resources for '" << card_path << "'"; + } + + LOG(FATAL) << "Failed to open primary graphics device."; + return base::FilePath(); // Not reached. +} + +class FindDrmDisplayHostById { + public: + explicit FindDrmDisplayHostById(int64_t display_id) + : display_id_(display_id) {} + + bool operator()(const scoped_ptr<DrmDisplayHost>& display) const { + return display->snapshot()->display_id() == display_id_; + } + + private: + int64_t display_id_; +}; + +} // namespace + DrmDisplayHostManager::DrmDisplayHostManager( - DrmGpuPlatformSupportHost* proxy, + GpuThreadAdapter* proxy, DeviceManager* device_manager, InputControllerEvdev* input_controller) - : sender_(new HostManagerIPC(proxy, this)), - core_(new DrmDisplayHostManagerCore(sender_.get(), - device_manager, - input_controller)) {} + : proxy_(proxy), + device_manager_(device_manager), + input_controller_(input_controller), + primary_graphics_card_path_(GetPrimaryDisplayCardPath()), + weak_ptr_factory_(this) { + { + // First device needs to be treated specially. We need to open this + // synchronously since the GPU process will need it to initialize the + // graphics state. + base::ThreadRestrictions::ScopedAllowIO allow_io; + + base::FilePath primary_graphics_card_path_sysfs = + MapDevPathToSysPath(primary_graphics_card_path_); + + primary_drm_device_handle_.reset(new DrmDeviceHandle()); + if (!primary_drm_device_handle_->Initialize( + primary_graphics_card_path_, primary_graphics_card_path_sysfs)) { + LOG(FATAL) << "Failed to open primary graphics card"; + return; + } + drm_devices_[primary_graphics_card_path_] = + primary_graphics_card_path_sysfs; + } + + device_manager_->AddObserver(this); + proxy_->RegisterHandlerForDrmDisplayHostManager(this); + proxy_->AddGpuThreadObserver(this); + + ScopedVector<HardwareDisplayControllerInfo> display_infos = + GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd()); + has_dummy_display_ = !display_infos.empty(); + for (size_t i = 0; i < display_infos.size(); ++i) { + displays_.push_back(make_scoped_ptr(new DrmDisplayHost( + proxy_, CreateDisplaySnapshotParams( + display_infos[i], primary_drm_device_handle_->fd(), + primary_drm_device_handle_->sys_path(), 0, gfx::Point()), + true /* is_dummy */))); + } +} DrmDisplayHostManager::~DrmDisplayHostManager() { + device_manager_->RemoveObserver(this); + proxy_->UnRegisterHandlerForDrmDisplayHostManager(); } DrmDisplayHost* DrmDisplayHostManager::GetDisplay(int64_t display_id) { - return core_->GetDisplay(display_id); + auto it = std::find_if(displays_.begin(), displays_.end(), + FindDrmDisplayHostById(display_id)); + if (it == displays_.end()) + return nullptr; + + return it->get(); } void DrmDisplayHostManager::AddDelegate(DrmNativeDisplayDelegate* delegate) { - core_->AddDelegate(delegate); + DCHECK(!delegate_); + delegate_ = delegate; } void DrmDisplayHostManager::RemoveDelegate(DrmNativeDisplayDelegate* delegate) { - core_->RemoveDelegate(delegate); + DCHECK_EQ(delegate_, delegate); + delegate_ = nullptr; } void DrmDisplayHostManager::TakeDisplayControl( const DisplayControlCallback& callback) { - core_->TakeDisplayControl(callback); + if (display_control_change_pending_) { + LOG(ERROR) << "TakeDisplayControl called while change already pending"; + callback.Run(false); + return; + } + + if (!display_externally_controlled_) { + LOG(ERROR) << "TakeDisplayControl called while display already owned"; + callback.Run(true); + return; + } + + take_display_control_callback_ = callback; + display_control_change_pending_ = true; + + if (!proxy_->GpuTakeDisplayControl()) + GpuTookDisplayControl(false); } void DrmDisplayHostManager::RelinquishDisplayControl( const DisplayControlCallback& callback) { - core_->RelinquishDisplayControl(callback); + if (display_control_change_pending_) { + LOG(ERROR) + << "RelinquishDisplayControl called while change already pending"; + callback.Run(false); + return; + } + + if (display_externally_controlled_) { + LOG(ERROR) << "RelinquishDisplayControl called while display not owned"; + callback.Run(true); + return; + } + + relinquish_display_control_callback_ = callback; + display_control_change_pending_ = true; + + if (!proxy_->GpuRelinquishDisplayControl()) + GpuRelinquishedDisplayControl(false); } void DrmDisplayHostManager::UpdateDisplays( const GetDisplaysCallback& callback) { - core_->UpdateDisplays(callback); + get_displays_callback_ = callback; + if (!proxy_->GpuRefreshNativeDisplays()) { + get_displays_callback_.Reset(); + RunUpdateDisplaysCallback(callback); + } } -void DrmDisplayHostManager::OnChannelEstablished( - int host_id, - scoped_refptr<base::SingleThreadTaskRunner> send_runner, - const base::Callback<void(IPC::Message*)>& send_callback) { - // The GPU thread may be in a different or the same process. - core_->GpuThreadStarted(); -} +void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent& event) { + if (event.device_type() != DeviceEvent::DISPLAY) + return; -void DrmDisplayHostManager::OnChannelDestroyed(int host_id) { + event_queue_.push(DisplayEvent(event.action_type(), event.path())); + ProcessEvent(); } -bool DrmDisplayHostManager::OnMessageReceived(const IPC::Message& message) { - bool handled = true; +void DrmDisplayHostManager::ProcessEvent() { + while (!event_queue_.empty() && !task_pending_) { + DisplayEvent event = event_queue_.front(); + event_queue_.pop(); + VLOG(1) << "Got display event " << kDisplayActionString[event.action_type] + << " for " << event.path.value(); + switch (event.action_type) { + case DeviceEvent::ADD: + if (drm_devices_.find(event.path) == drm_devices_.end()) { + task_pending_ = base::WorkerPool::PostTask( + FROM_HERE, + base::Bind(&OpenDeviceOnWorkerThread, event.path, + base::ThreadTaskRunnerHandle::Get(), + base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice, + weak_ptr_factory_.GetWeakPtr())), + false /* task_is_slow */); + } + break; + case DeviceEvent::CHANGE: + task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&DrmDisplayHostManager::OnUpdateGraphicsDevice, + weak_ptr_factory_.GetWeakPtr())); + break; + case DeviceEvent::REMOVE: + DCHECK(event.path != primary_graphics_card_path_) + << "Removing primary graphics card"; + auto it = drm_devices_.find(event.path); + if (it != drm_devices_.end()) { + task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&DrmDisplayHostManager::OnRemoveGraphicsDevice, + weak_ptr_factory_.GetWeakPtr(), it->second)); + drm_devices_.erase(it); + } + break; + } + } +} - IPC_BEGIN_MESSAGE_MAP(DrmDisplayHostManager, message) - IPC_MESSAGE_HANDLER(OzoneHostMsg_UpdateNativeDisplays, OnUpdateNativeDisplays) - IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayConfigured, OnDisplayConfigured) - IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateReceived, OnHDCPStateReceived) - IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateUpdated, OnHDCPStateUpdated) - IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlTaken, OnTakeDisplayControl) - IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlRelinquished, - OnRelinquishDisplayControl) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() +void DrmDisplayHostManager::OnAddGraphicsDevice( + const base::FilePath& dev_path, + const base::FilePath& sys_path, + scoped_ptr<DrmDeviceHandle> handle) { + if (handle->IsValid()) { + drm_devices_[dev_path] = sys_path; + proxy_->GpuAddGraphicsDevice(sys_path, + base::FileDescriptor(handle->PassFD())); + NotifyDisplayDelegate(); + } + + task_pending_ = false; + ProcessEvent(); +} - return handled; +void DrmDisplayHostManager::OnUpdateGraphicsDevice() { + NotifyDisplayDelegate(); + task_pending_ = false; + ProcessEvent(); } -void DrmDisplayHostManager::OnUpdateNativeDisplays( - const std::vector<DisplaySnapshot_Params>& params) { - core_->GpuHasUpdatedNativeDisplays(params); +void DrmDisplayHostManager::OnRemoveGraphicsDevice( + const base::FilePath& sys_path) { + proxy_->GpuRemoveGraphicsDevice(sys_path); + NotifyDisplayDelegate(); + task_pending_ = false; + ProcessEvent(); } -void DrmDisplayHostManager::OnDisplayConfigured(int64_t display_id, - bool status) { - core_->GpuConfiguredDisplay(display_id, status); +void DrmDisplayHostManager::OnGpuThreadReady() { + // If in the middle of a configuration, just respond with the old list of + // displays. This is fine, since after the DRM resources are initialized and + // IPC-ed to the GPU NotifyDisplayDelegate() is called to let the display + // delegate know that the display configuration changed and it needs to + // update it again. + if (!get_displays_callback_.is_null()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback, + weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); + get_displays_callback_.Reset(); + } + + // Signal that we're taking DRM master since we're going through the + // initialization process again and we'll take all the available resources. + if (!take_display_control_callback_.is_null()) + GpuTookDisplayControl(true); + + if (!relinquish_display_control_callback_.is_null()) + GpuRelinquishedDisplayControl(false); + + scoped_ptr<DrmDeviceHandle> handle = std::move(primary_drm_device_handle_); + { + base::ThreadRestrictions::ScopedAllowIO allow_io; + + drm_devices_.clear(); + drm_devices_[primary_graphics_card_path_] = + MapDevPathToSysPath(primary_graphics_card_path_); + + if (!handle) { + handle.reset(new DrmDeviceHandle()); + if (!handle->Initialize(primary_graphics_card_path_, + drm_devices_[primary_graphics_card_path_])) + LOG(FATAL) << "Failed to open primary graphics card"; + } + } + + // 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())); + + device_manager_->ScanDevices(this); + NotifyDisplayDelegate(); } -void DrmDisplayHostManager::OnHDCPStateReceived(int64_t display_id, - bool status, - HDCPState state) { - core_->GpuReceivedHDCPState(display_id, status, state); +void DrmDisplayHostManager::OnGpuThreadRetired() {} + +void DrmDisplayHostManager::GpuHasUpdatedNativeDisplays( + const std::vector<DisplaySnapshot_Params>& params) { + std::vector<scoped_ptr<DrmDisplayHost>> old_displays; + displays_.swap(old_displays); + for (size_t i = 0; i < params.size(); ++i) { + auto it = std::find_if(old_displays.begin(), old_displays.end(), + FindDrmDisplayHostById(params[i].display_id)); + if (it == old_displays.end()) { + displays_.push_back(make_scoped_ptr( + new DrmDisplayHost(proxy_, params[i], false /* is_dummy */))); + } else { + (*it)->UpdateDisplaySnapshot(params[i]); + displays_.push_back(std::move(*it)); + old_displays.erase(it); + } + } + + if (!get_displays_callback_.is_null()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback, + weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); + get_displays_callback_.Reset(); + } } -void DrmDisplayHostManager::OnHDCPStateUpdated(int64_t display_id, - bool status) { - core_->GpuUpdatedHDCPState(display_id, status); +void DrmDisplayHostManager::GpuConfiguredDisplay(int64_t display_id, + bool status) { + DrmDisplayHost* display = GetDisplay(display_id); + if (display) + display->OnDisplayConfigured(status); + else + LOG(ERROR) << "Couldn't find display with id=" << display_id; } -void DrmDisplayHostManager::OnTakeDisplayControl(bool status) { - core_->GpuTookDisplayControl(status); +void DrmDisplayHostManager::GpuReceivedHDCPState(int64_t display_id, + bool status, + HDCPState state) { + DrmDisplayHost* display = GetDisplay(display_id); + if (display) + display->OnHDCPStateReceived(status, state); + else + LOG(ERROR) << "Couldn't find display with id=" << display_id; } -void DrmDisplayHostManager::OnRelinquishDisplayControl(bool status) { - core_->GpuRelinquishedDisplayControl(status); +void DrmDisplayHostManager::GpuUpdatedHDCPState(int64_t display_id, + bool status) { + DrmDisplayHost* display = GetDisplay(display_id); + if (display) + display->OnHDCPStateUpdated(status); + else + LOG(ERROR) << "Couldn't find display with id=" << display_id; } -DrmDisplayHostManager::HostManagerIPC::HostManagerIPC( - DrmGpuPlatformSupportHost* proxy, - DrmDisplayHostManager* parent) - : proxy_(proxy), parent_(parent) {} +void DrmDisplayHostManager::GpuTookDisplayControl(bool status) { + if (take_display_control_callback_.is_null()) { + LOG(ERROR) << "No callback for take display control"; + return; + } -DrmDisplayHostManager::HostManagerIPC::~HostManagerIPC() { - proxy_->UnregisterHandler(parent_); -} + DCHECK(display_externally_controlled_); + DCHECK(display_control_change_pending_); -void DrmDisplayHostManager::HostManagerIPC::RegisterHandler() { - proxy_->RegisterHandler(parent_); -} + if (status) { + input_controller_->SetInputDevicesEnabled(true); + display_externally_controlled_ = false; + } -DrmGpuPlatformSupportHost* -DrmDisplayHostManager::HostManagerIPC::GetGpuPlatformSupportHost() { - return proxy_; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(take_display_control_callback_, status)); + take_display_control_callback_.Reset(); + display_control_change_pending_ = false; } -bool DrmDisplayHostManager::HostManagerIPC::RefreshNativeDisplays() { - return proxy_->Send(new OzoneGpuMsg_RefreshNativeDisplays()); -} +void DrmDisplayHostManager::GpuRelinquishedDisplayControl(bool status) { + if (relinquish_display_control_callback_.is_null()) { + LOG(ERROR) << "No callback for relinquish display control"; + return; + } -bool DrmDisplayHostManager::HostManagerIPC::TakeDisplayControl() { - return proxy_->Send(new OzoneGpuMsg_TakeDisplayControl()); -} + DCHECK(!display_externally_controlled_); + DCHECK(display_control_change_pending_); -bool DrmDisplayHostManager::HostManagerIPC::RelinquishDisplayControl() { - return proxy_->Send(new OzoneGpuMsg_RelinquishDisplayControl()); + if (status) { + input_controller_->SetInputDevicesEnabled(false); + display_externally_controlled_ = true; + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(relinquish_display_control_callback_, status)); + relinquish_display_control_callback_.Reset(); + display_control_change_pending_ = false; } -bool DrmDisplayHostManager::HostManagerIPC::AddGraphicsDevice( - const base::FilePath& path, - base::FileDescriptor fd) { - return proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(path, fd)); +void DrmDisplayHostManager::RunUpdateDisplaysCallback( + const GetDisplaysCallback& callback) const { + std::vector<DisplaySnapshot*> snapshots; + for (const auto& display : displays_) + snapshots.push_back(display->snapshot()); + + callback.Run(snapshots); } -bool DrmDisplayHostManager::HostManagerIPC::RemoveGraphicsDevice( - const base::FilePath& path) { - return proxy_->Send(new OzoneGpuMsg_RemoveGraphicsDevice(path)); +void DrmDisplayHostManager::NotifyDisplayDelegate() const { + if (delegate_) + delegate_->OnConfigurationChanged(); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h index 1079e199628..fbe10ef6394 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h @@ -7,82 +7,136 @@ #include <stdint.h> +#include <map> +#include <queue> + +#include "base/file_descriptor_posix.h" +#include "base/files/scoped_file.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" #include "ui/display/types/native_display_delegate.h" -#include "ui/ozone/platform/drm/host/drm_display_host_manager_core.h" -#include "ui/ozone/public/gpu_platform_support_host.h" +#include "ui/events/ozone/device/device_event.h" +#include "ui/events/ozone/device/device_event_observer.h" +#include "ui/events/ozone/evdev/event_factory_evdev.h" +#include "ui/ozone/platform/drm/host/gpu_thread_observer.h" namespace ui { class DeviceManager; +class DrmDeviceHandle; class DrmDisplayHost; +class DrmDisplayHostManager; class DrmGpuPlatformSupportHost; class DrmNativeDisplayDelegate; +class GpuThreadAdapter; struct DisplaySnapshot_Params; -class DrmDisplayHostManager : public GpuPlatformSupportHost { +// The portion of the DrmDisplayHostManager implementation that is agnostic +// in how its communication with GPU-specific functionality is implemented. +// This is used from both the IPC and the in-process versions in MUS. +class DrmDisplayHostManager : public DeviceEventObserver, GpuThreadObserver { public: - DrmDisplayHostManager(DrmGpuPlatformSupportHost* proxy, + DrmDisplayHostManager(GpuThreadAdapter* proxy, DeviceManager* device_manager, InputControllerEvdev* input_controller); ~DrmDisplayHostManager() override; DrmDisplayHost* GetDisplay(int64_t display_id); + + // External API. void AddDelegate(DrmNativeDisplayDelegate* delegate); void RemoveDelegate(DrmNativeDisplayDelegate* delegate); - void TakeDisplayControl(const DisplayControlCallback& callback); void RelinquishDisplayControl(const DisplayControlCallback& callback); void UpdateDisplays(const GetDisplaysCallback& callback); - // IPC::Listener (by way of GpuPlatformSupportHost) overrides: - void OnChannelEstablished( - int host_id, - scoped_refptr<base::SingleThreadTaskRunner> send_runner, - const base::Callback<void(IPC::Message*)>& send_callback) override; - void OnChannelDestroyed(int host_id) override; - bool OnMessageReceived(const IPC::Message& message) override; + // DeviceEventObserver overrides: + void OnDeviceEvent(const DeviceEvent& event) override; + + // GpuThreadObserver overrides: + void OnGpuThreadReady() override; + void OnGpuThreadRetired() override; + + // Communication-free implementations of actions performed in response to + // messages from the GPU thread. + void GpuHasUpdatedNativeDisplays( + const std::vector<DisplaySnapshot_Params>& displays); + void GpuConfiguredDisplay(int64_t display_id, bool status); + void GpuReceivedHDCPState(int64_t display_id, bool status, HDCPState state); + void GpuUpdatedHDCPState(int64_t display_id, bool status); + void GpuTookDisplayControl(bool status); + void GpuRelinquishedDisplayControl(bool status); private: - // Concrete implementation of sending messages to the GPU thread hosted - // functionality. - class HostManagerIPC : public DrmDisplayHostManagerProxy { - public: - HostManagerIPC(DrmGpuPlatformSupportHost* proxy, - DrmDisplayHostManager* parent); - ~HostManagerIPC() override; - - void RegisterHandler() override; - DrmGpuPlatformSupportHost* GetGpuPlatformSupportHost() override; - bool TakeDisplayControl() override; - bool RefreshNativeDisplays() override; - bool RelinquishDisplayControl() override; - bool AddGraphicsDevice(const base::FilePath& path, - base::FileDescriptor fd) override; - bool RemoveGraphicsDevice(const base::FilePath& path) override; - - private: - DrmGpuPlatformSupportHost* proxy_; - DrmDisplayHostManager* parent_; - - DISALLOW_COPY_AND_ASSIGN(HostManagerIPC); + struct DisplayEvent { + DisplayEvent(DeviceEvent::ActionType action_type, + const base::FilePath& path) + : action_type(action_type), path(path) {} + + DeviceEvent::ActionType action_type; + base::FilePath path; }; - // IPC Entry points. - void OnUpdateNativeDisplays( - const std::vector<DisplaySnapshot_Params>& displays); - void OnDisplayConfigured(int64_t display_id, bool status); - void OnHDCPStateReceived(int64_t display_id, bool status, HDCPState state); - void OnHDCPStateUpdated(int64_t display_id, bool status); - void OnTakeDisplayControl(bool status); - void OnRelinquishDisplayControl(bool status); + // Handle hotplug events sequentially. + void ProcessEvent(); + + // Called as a result of finishing to process the display hotplug event. These + // are responsible for dequing the event and scheduling the next event. + void OnAddGraphicsDevice(const base::FilePath& path, + const base::FilePath& sysfs_path, + scoped_ptr<DrmDeviceHandle> handle); + void OnUpdateGraphicsDevice(); + void OnRemoveGraphicsDevice(const base::FilePath& path); + + void RunUpdateDisplaysCallback(const GetDisplaysCallback& callback) const; + + void NotifyDisplayDelegate() const; + + GpuThreadAdapter* proxy_; // Not owned. + DeviceManager* device_manager_; // Not owned. + InputControllerEvdev* input_controller_; // Not owned. + + DrmNativeDisplayDelegate* delegate_ = nullptr; // Not owned. + + // File path for the primary graphics card which is opened by default in the + // GPU process. We'll avoid opening this in hotplug events since it will race + // with the GPU process trying to open it and aquire DRM master. + base::FilePath primary_graphics_card_path_; + + // Keeps track if there is a dummy display. This happens on initialization + // when there is no connection to the GPU to update the displays. + bool has_dummy_display_ = false; + + std::vector<scoped_ptr<DrmDisplayHost>> displays_; + + GetDisplaysCallback get_displays_callback_; + + bool display_externally_controlled_ = false; + bool display_control_change_pending_ = false; + DisplayControlCallback take_display_control_callback_; + DisplayControlCallback relinquish_display_control_callback_; + + // Used to serialize display event processing. This is done since + // opening/closing DRM devices cannot be done on the UI thread and are handled + // on a worker thread. Thus, we need to queue events in order to process them + // in the correct order. + std::queue<DisplayEvent> event_queue_; + + // True if a display event is currently being processed on a worker thread. + bool task_pending_ = false; + + // Keeps track of all the active DRM devices. The key is the device path, the + // value is the sysfs path which has been resolved from the device path. + std::map<base::FilePath, base::FilePath> drm_devices_; - // Sends messages to the GPU thread. - scoped_ptr<HostManagerIPC> sender_; + // This is used to cache the primary DRM device until the channel is + // established. + scoped_ptr<DrmDeviceHandle> primary_drm_device_handle_; - // Implementation without messaging functionality. - scoped_ptr<DrmDisplayHostManagerCore> core_; + base::WeakPtrFactory<DrmDisplayHostManager> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(DrmDisplayHostManager); }; diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager_core.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager_core.cc deleted file mode 100644 index 5d17cc4197c..00000000000 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager_core.cc +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/ozone/platform/drm/host/drm_display_host_manager_core.h" - -#include <fcntl.h> -#include <stddef.h> -#include <xf86drm.h> -#include <utility> - -#include "base/files/file_enumerator.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/thread_task_runner_handle.h" -#include "base/threading/thread_restrictions.h" -#include "base/threading/worker_pool.h" -#include "ui/display/types/display_snapshot.h" -#include "ui/events/ozone/device/device_event.h" -#include "ui/events/ozone/device/device_manager.h" -#include "ui/ozone/common/display_util.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_device_handle.h" -#include "ui/ozone/platform/drm/host/drm_display_host.h" -#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h" -#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h" - -namespace ui { - -namespace { - -typedef base::Callback<void(const base::FilePath&, - const base::FilePath&, - scoped_ptr<DrmDeviceHandle>)> - OnOpenDeviceReplyCallback; - -const char kDefaultGraphicsCardPattern[] = "/dev/dri/card%d"; -const char kVgemDevDriCardPath[] = "/dev/dri/"; -const char kVgemSysCardPath[] = "/sys/bus/platform/devices/vgem/drm/"; - -const char* kDisplayActionString[] = { - "ADD", "REMOVE", "CHANGE", -}; - -// Find sysfs device path for the given device path. -base::FilePath MapDevPathToSysPath(const base::FilePath& device_path) { - // |device_path| looks something like /dev/dri/card0. We take the basename of - // that (card0) and append it to /sys/class/drm. /sys/class/drm/card0 is a - // symlink that points to something like - // /sys/devices/pci0000:00/0000:00:02.0/0000:05:00.0/drm/card0, which exposes - // some metadata about the attached device. - return base::MakeAbsoluteFilePath( - base::FilePath("/sys/class/drm").Append(device_path.BaseName())); -} - -void OpenDeviceOnWorkerThread( - const base::FilePath& device_path, - const scoped_refptr<base::TaskRunner>& reply_runner, - const OnOpenDeviceReplyCallback& callback) { - base::FilePath sys_path = MapDevPathToSysPath(device_path); - - scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle()); - handle->Initialize(device_path, sys_path); - reply_runner->PostTask(FROM_HERE, - base::Bind(callback, device_path, sys_path, - base::Passed(std::move(handle)))); -} - -base::FilePath GetPrimaryDisplayCardPath() { - struct drm_mode_card_res res; - for (int i = 0; /* end on first card# that does not exist */; i++) { - std::string card_path = base::StringPrintf(kDefaultGraphicsCardPattern, i); - - if (access(card_path.c_str(), F_OK) != 0) - break; - - int fd = open(card_path.c_str(), O_RDWR | O_CLOEXEC); - if (fd < 0) { - VPLOG(1) << "Failed to open '" << card_path << "'"; - continue; - } - - memset(&res, 0, sizeof(struct drm_mode_card_res)); - int ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res); - close(fd); - if (ret == 0 && res.count_crtcs > 0) { - return base::FilePath(card_path); - } - - VPLOG_IF(1, ret) << "Failed to get DRM resources for '" << card_path << "'"; - } - - LOG(FATAL) << "Failed to open primary graphics device."; - return base::FilePath(); // Not reached. -} - -base::FilePath GetVgemCardPath() { - base::FileEnumerator file_iter(base::FilePath(kVgemSysCardPath), false, - base::FileEnumerator::DIRECTORIES, - FILE_PATH_LITERAL("card*")); - - while (!file_iter.Next().empty()) { - // Inspect the card%d directories in the directory and extract the filename. - std::string vgem_card_path = - kVgemDevDriCardPath + file_iter.GetInfo().GetName().BaseName().value(); - DVLOG(1) << "VGEM card path is " << vgem_card_path; - return base::FilePath(vgem_card_path); - } - DVLOG(1) << "Don't support VGEM"; - return base::FilePath(); -} - -class FindDrmDisplayHostById { - public: - explicit FindDrmDisplayHostById(int64_t display_id) - : display_id_(display_id) {} - - bool operator()(const scoped_ptr<DrmDisplayHost>& display) const { - return display->snapshot()->display_id() == display_id_; - } - - private: - int64_t display_id_; -}; - -} // namespace - -DrmDisplayHostManagerProxy::~DrmDisplayHostManagerProxy() {} - -DrmDisplayHostManagerCore::DrmDisplayHostManagerCore( - DrmDisplayHostManagerProxy* proxy, - DeviceManager* device_manager, - InputControllerEvdev* input_controller) - : proxy_(proxy), - device_manager_(device_manager), - input_controller_(input_controller), - primary_graphics_card_path_(GetPrimaryDisplayCardPath()), - weak_ptr_factory_(this) { - { - // First device needs to be treated specially. We need to open this - // synchronously since the GPU process will need it to initialize the - // graphics state. - base::ThreadRestrictions::ScopedAllowIO allow_io; - - base::FilePath primary_graphics_card_path_sysfs = - MapDevPathToSysPath(primary_graphics_card_path_); - - primary_drm_device_handle_.reset(new DrmDeviceHandle()); - if (!primary_drm_device_handle_->Initialize( - primary_graphics_card_path_, primary_graphics_card_path_sysfs)) { - LOG(FATAL) << "Failed to open primary graphics card"; - return; - } - drm_devices_[primary_graphics_card_path_] = - primary_graphics_card_path_sysfs; - - vgem_card_path_ = GetVgemCardPath(); - } - - device_manager_->AddObserver(this); - proxy_->RegisterHandler(); - - ScopedVector<HardwareDisplayControllerInfo> display_infos = - GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd()); - has_dummy_display_ = !display_infos.empty(); - for (size_t i = 0; i < display_infos.size(); ++i) { - displays_.push_back(make_scoped_ptr(new DrmDisplayHost( - proxy_->GetGpuPlatformSupportHost(), - CreateDisplaySnapshotParams( - display_infos[i], primary_drm_device_handle_->fd(), - primary_drm_device_handle_->sys_path(), 0, gfx::Point()), - true /* is_dummy */))); - } -} - -DrmDisplayHostManagerCore::~DrmDisplayHostManagerCore() { - device_manager_->RemoveObserver(this); -} - -DrmDisplayHost* DrmDisplayHostManagerCore::GetDisplay(int64_t display_id) { - auto it = std::find_if(displays_.begin(), displays_.end(), - FindDrmDisplayHostById(display_id)); - if (it == displays_.end()) - return nullptr; - - return it->get(); -} - -void DrmDisplayHostManagerCore::AddDelegate( - DrmNativeDisplayDelegate* delegate) { - DCHECK(!delegate_); - delegate_ = delegate; -} - -void DrmDisplayHostManagerCore::RemoveDelegate( - DrmNativeDisplayDelegate* delegate) { - DCHECK_EQ(delegate_, delegate); - delegate_ = nullptr; -} - -void DrmDisplayHostManagerCore::TakeDisplayControl( - const DisplayControlCallback& callback) { - if (display_control_change_pending_) { - LOG(ERROR) << "TakeDisplayControl called while change already pending"; - callback.Run(false); - return; - } - - if (!display_externally_controlled_) { - LOG(ERROR) << "TakeDisplayControl called while display already owned"; - callback.Run(true); - return; - } - - take_display_control_callback_ = callback; - display_control_change_pending_ = true; - - if (!proxy_->TakeDisplayControl()) - GpuTookDisplayControl(false); -} - -void DrmDisplayHostManagerCore::RelinquishDisplayControl( - const DisplayControlCallback& callback) { - if (display_control_change_pending_) { - LOG(ERROR) - << "RelinquishDisplayControl called while change already pending"; - callback.Run(false); - return; - } - - if (display_externally_controlled_) { - LOG(ERROR) << "RelinquishDisplayControl called while display not owned"; - callback.Run(true); - return; - } - - relinquish_display_control_callback_ = callback; - display_control_change_pending_ = true; - - if (!proxy_->RelinquishDisplayControl()) - GpuRelinquishedDisplayControl(false); -} - -void DrmDisplayHostManagerCore::UpdateDisplays( - const GetDisplaysCallback& callback) { - get_displays_callback_ = callback; - if (!proxy_->RefreshNativeDisplays()) { - get_displays_callback_.Reset(); - RunUpdateDisplaysCallback(callback); - } -} - -void DrmDisplayHostManagerCore::OnDeviceEvent(const DeviceEvent& event) { - if (event.device_type() != DeviceEvent::DISPLAY) - return; - - event_queue_.push(DisplayEvent(event.action_type(), event.path())); - ProcessEvent(); -} - -void DrmDisplayHostManagerCore::ProcessEvent() { - while (!event_queue_.empty() && !task_pending_) { - DisplayEvent event = event_queue_.front(); - event_queue_.pop(); - VLOG(1) << "Got display event " << kDisplayActionString[event.action_type] - << " for " << event.path.value(); - switch (event.action_type) { - case DeviceEvent::ADD: - if (event.path == vgem_card_path_) - continue; - if (drm_devices_.find(event.path) == drm_devices_.end()) { - task_pending_ = base::WorkerPool::PostTask( - FROM_HERE, - base::Bind( - &OpenDeviceOnWorkerThread, event.path, - base::ThreadTaskRunnerHandle::Get(), - base::Bind(&DrmDisplayHostManagerCore::OnAddGraphicsDevice, - weak_ptr_factory_.GetWeakPtr())), - false /* task_is_slow */); - } - break; - case DeviceEvent::CHANGE: - task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&DrmDisplayHostManagerCore::OnUpdateGraphicsDevice, - weak_ptr_factory_.GetWeakPtr())); - break; - case DeviceEvent::REMOVE: - DCHECK(event.path != primary_graphics_card_path_) - << "Removing primary graphics card"; - DCHECK(event.path != vgem_card_path_) << "Removing VGEM device"; - auto it = drm_devices_.find(event.path); - if (it != drm_devices_.end()) { - task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&DrmDisplayHostManagerCore::OnRemoveGraphicsDevice, - weak_ptr_factory_.GetWeakPtr(), it->second)); - drm_devices_.erase(it); - } - break; - } - } -} - -void DrmDisplayHostManagerCore::OnAddGraphicsDevice( - const base::FilePath& dev_path, - const base::FilePath& sys_path, - scoped_ptr<DrmDeviceHandle> handle) { - if (handle->IsValid()) { - drm_devices_[dev_path] = sys_path; - proxy_->AddGraphicsDevice(sys_path, base::FileDescriptor(handle->PassFD())); - NotifyDisplayDelegate(); - } - - task_pending_ = false; - ProcessEvent(); -} - -void DrmDisplayHostManagerCore::OnUpdateGraphicsDevice() { - NotifyDisplayDelegate(); - task_pending_ = false; - ProcessEvent(); -} - -void DrmDisplayHostManagerCore::OnRemoveGraphicsDevice( - const base::FilePath& sys_path) { - proxy_->RemoveGraphicsDevice(sys_path); - NotifyDisplayDelegate(); - task_pending_ = false; - ProcessEvent(); -} - -void DrmDisplayHostManagerCore::GpuThreadStarted() { - // If in the middle of a configuration, just respond with the old list of - // displays. This is fine, since after the DRM resources are initialized and - // IPC-ed to the GPU NotifyDisplayDelegate() is called to let the display - // delegate know that the display configuration changed and it needs to - // update it again. - if (!get_displays_callback_.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&DrmDisplayHostManagerCore::RunUpdateDisplaysCallback, - weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); - get_displays_callback_.Reset(); - } - - // Signal that we're taking DRM master since we're going through the - // initialization process again and we'll take all the available resources. - if (!take_display_control_callback_.is_null()) - GpuTookDisplayControl(true); - - if (!relinquish_display_control_callback_.is_null()) - GpuRelinquishedDisplayControl(false); - - scoped_ptr<DrmDeviceHandle> handle = std::move(primary_drm_device_handle_); - { - base::ThreadRestrictions::ScopedAllowIO allow_io; - - drm_devices_.clear(); - drm_devices_[primary_graphics_card_path_] = - MapDevPathToSysPath(primary_graphics_card_path_); - - if (!handle) { - handle.reset(new DrmDeviceHandle()); - if (!handle->Initialize(primary_graphics_card_path_, - drm_devices_[primary_graphics_card_path_])) - LOG(FATAL) << "Failed to open primary graphics card"; - } - } - - // Send the primary device first since this is used to initialize graphics - // state. - proxy_->AddGraphicsDevice(drm_devices_[primary_graphics_card_path_], - base::FileDescriptor(handle->PassFD())); - - device_manager_->ScanDevices(this); - NotifyDisplayDelegate(); -} - -void DrmDisplayHostManagerCore::GpuHasUpdatedNativeDisplays( - const std::vector<DisplaySnapshot_Params>& params) { - std::vector<scoped_ptr<DrmDisplayHost>> old_displays; - displays_.swap(old_displays); - for (size_t i = 0; i < params.size(); ++i) { - auto it = std::find_if(old_displays.begin(), old_displays.end(), - FindDrmDisplayHostById(params[i].display_id)); - if (it == old_displays.end()) { - displays_.push_back(make_scoped_ptr( - new DrmDisplayHost(proxy_->GetGpuPlatformSupportHost(), params[i], - false /* is_dummy */))); - } else { - (*it)->UpdateDisplaySnapshot(params[i]); - displays_.push_back(std::move(*it)); - old_displays.erase(it); - } - } - - if (!get_displays_callback_.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&DrmDisplayHostManagerCore::RunUpdateDisplaysCallback, - weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); - get_displays_callback_.Reset(); - } -} - -void DrmDisplayHostManagerCore::GpuConfiguredDisplay(int64_t display_id, - bool status) { - DrmDisplayHost* display = GetDisplay(display_id); - if (display) - display->OnDisplayConfigured(status); - else - LOG(ERROR) << "Couldn't find display with id=" << display_id; -} - -void DrmDisplayHostManagerCore::GpuReceivedHDCPState(int64_t display_id, - bool status, - HDCPState state) { - DrmDisplayHost* display = GetDisplay(display_id); - if (display) - display->OnHDCPStateReceived(status, state); - else - LOG(ERROR) << "Couldn't find display with id=" << display_id; -} - -void DrmDisplayHostManagerCore::GpuUpdatedHDCPState(int64_t display_id, - bool status) { - DrmDisplayHost* display = GetDisplay(display_id); - if (display) - display->OnHDCPStateUpdated(status); - else - LOG(ERROR) << "Couldn't find display with id=" << display_id; -} - -void DrmDisplayHostManagerCore::GpuTookDisplayControl(bool status) { - if (take_display_control_callback_.is_null()) { - LOG(ERROR) << "No callback for take display control"; - return; - } - - DCHECK(display_externally_controlled_); - DCHECK(display_control_change_pending_); - - if (status) { - input_controller_->SetInputDevicesEnabled(true); - display_externally_controlled_ = false; - } - - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(take_display_control_callback_, status)); - take_display_control_callback_.Reset(); - display_control_change_pending_ = false; -} - -void DrmDisplayHostManagerCore::GpuRelinquishedDisplayControl(bool status) { - if (relinquish_display_control_callback_.is_null()) { - LOG(ERROR) << "No callback for relinquish display control"; - return; - } - - DCHECK(!display_externally_controlled_); - DCHECK(display_control_change_pending_); - - if (status) { - input_controller_->SetInputDevicesEnabled(false); - display_externally_controlled_ = true; - } - - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(relinquish_display_control_callback_, status)); - relinquish_display_control_callback_.Reset(); - display_control_change_pending_ = false; -} - -void DrmDisplayHostManagerCore::RunUpdateDisplaysCallback( - const GetDisplaysCallback& callback) const { - std::vector<DisplaySnapshot*> snapshots; - for (const auto& display : displays_) - snapshots.push_back(display->snapshot()); - - callback.Run(snapshots); -} - -void DrmDisplayHostManagerCore::NotifyDisplayDelegate() const { - if (delegate_) - delegate_->OnConfigurationChanged(); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager_core.h b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager_core.h deleted file mode 100644 index c9f9f316665..00000000000 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager_core.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_MANAGER_CORE_H_ -#define UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_MANAGER_CORE_H_ - -#include <stdint.h> - -#include <map> -#include <queue> - -#include "base/file_descriptor_posix.h" -#include "base/files/scoped_file.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "base/memory/weak_ptr.h" -#include "ui/display/types/native_display_delegate.h" -#include "ui/events/ozone/device/device_event.h" -#include "ui/events/ozone/device/device_event_observer.h" -#include "ui/events/ozone/evdev/event_factory_evdev.h" -#include "ui/ozone/public/gpu_platform_support_host.h" - -namespace ui { - -class DeviceManager; -class DrmDeviceHandle; -class DrmDisplayHost; -class DrmDisplayHostManagerCore; -class DrmGpuPlatformSupportHost; -class DrmNativeDisplayDelegate; - -struct DisplaySnapshot_Params; - -// The concrete implementation of DrmDisplayHostManagerCoreProxy contains all -// the -// necessary code for the DrmDisplayHostManagerCoreCore to communicate with -// the GPU child thread whether by IPC or thead-hop. -class DrmDisplayHostManagerProxy { - public: - virtual ~DrmDisplayHostManagerProxy(); - virtual void RegisterHandler() = 0; - virtual DrmGpuPlatformSupportHost* GetGpuPlatformSupportHost() = 0; - virtual bool TakeDisplayControl() = 0; - virtual bool RefreshNativeDisplays() = 0; - virtual bool RelinquishDisplayControl() = 0; - virtual bool AddGraphicsDevice(const base::FilePath& path, - base::FileDescriptor fd) = 0; - virtual bool RemoveGraphicsDevice(const base::FilePath& path) = 0; -}; - -// The portion of the DrmDisplayHostManagerCore implementation that is agnostic -// in how its communication with GPU-specific functionality is implemented. -// This is used from both the IPC and the in-process versions in MUS. -class DrmDisplayHostManagerCore : public DeviceEventObserver { - public: - DrmDisplayHostManagerCore(DrmDisplayHostManagerProxy* proxy, - DeviceManager* device_manager, - InputControllerEvdev* input_controller); - ~DrmDisplayHostManagerCore() override; - - DrmDisplayHost* GetDisplay(int64_t display_id); - - // External API. - void AddDelegate(DrmNativeDisplayDelegate* delegate); - void RemoveDelegate(DrmNativeDisplayDelegate* delegate); - void TakeDisplayControl(const DisplayControlCallback& callback); - void RelinquishDisplayControl(const DisplayControlCallback& callback); - void UpdateDisplays(const GetDisplaysCallback& callback); - - // DeviceEventObserver overrides: - void OnDeviceEvent(const DeviceEvent& event) override; - - // Communication-free implementations of actions performed in response to - // messages from the GPU thread. - void GpuThreadStarted(); - void GpuHasUpdatedNativeDisplays( - const std::vector<DisplaySnapshot_Params>& displays); - void GpuConfiguredDisplay(int64_t display_id, bool status); - void GpuReceivedHDCPState(int64_t display_id, bool status, HDCPState state); - void GpuUpdatedHDCPState(int64_t display_id, bool status); - void GpuTookDisplayControl(bool status); - void GpuRelinquishedDisplayControl(bool status); - - private: - struct DisplayEvent { - DisplayEvent(DeviceEvent::ActionType action_type, - const base::FilePath& path) - : action_type(action_type), path(path) {} - - DeviceEvent::ActionType action_type; - base::FilePath path; - }; - - // Handle hotplug events sequentially. - void ProcessEvent(); - - // Called as a result of finishing to process the display hotplug event. These - // are responsible for dequing the event and scheduling the next event. - void OnAddGraphicsDevice(const base::FilePath& path, - const base::FilePath& sysfs_path, - scoped_ptr<DrmDeviceHandle> handle); - void OnUpdateGraphicsDevice(); - void OnRemoveGraphicsDevice(const base::FilePath& path); - - void RunUpdateDisplaysCallback(const GetDisplaysCallback& callback) const; - - void NotifyDisplayDelegate() const; - - DrmDisplayHostManagerProxy* proxy_; // Not owned. - DeviceManager* device_manager_; // Not owned. - InputControllerEvdev* input_controller_; // Not owned. - - DrmNativeDisplayDelegate* delegate_ = nullptr; // Not owned. - - // File path for the primary graphics card which is opened by default in the - // GPU process. We'll avoid opening this in hotplug events since it will race - // with the GPU process trying to open it and aquire DRM master. - base::FilePath primary_graphics_card_path_; - - // File path for virtual gem (VGEM) device. - base::FilePath vgem_card_path_; - - // Keeps track if there is a dummy display. This happens on initialization - // when there is no connection to the GPU to update the displays. - bool has_dummy_display_ = false; - - std::vector<scoped_ptr<DrmDisplayHost>> displays_; - - GetDisplaysCallback get_displays_callback_; - - bool display_externally_controlled_ = false; - bool display_control_change_pending_ = false; - DisplayControlCallback take_display_control_callback_; - DisplayControlCallback relinquish_display_control_callback_; - - // Used to serialize display event processing. This is done since - // opening/closing DRM devices cannot be done on the UI thread and are handled - // on a worker thread. Thus, we need to queue events in order to process them - // in the correct order. - std::queue<DisplayEvent> event_queue_; - - // True if a display event is currently being processed on a worker thread. - bool task_pending_ = false; - - // Keeps track of all the active DRM devices. The key is the device path, the - // value is the sysfs path which has been resolved from the device path. - std::map<base::FilePath, base::FilePath> drm_devices_; - - // This is used to cache the primary DRM device until the channel is - // established. - scoped_ptr<DrmDeviceHandle> primary_drm_device_handle_; - - base::WeakPtrFactory<DrmDisplayHostManagerCore> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(DrmDisplayHostManagerCore); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_DRM_HOST_DRM_DISPLAY_HOST_MANAGER_CORE_H_ 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 cbf759721e8..9f42ec2024f 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,44 +9,91 @@ #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/host/channel_observer.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" +#include "ui/ozone/platform/drm/host/drm_overlay_manager.h" +#include "ui/ozone/platform/drm/host/gpu_thread_observer.h" namespace ui { -DrmGpuPlatformSupportHost::DrmGpuPlatformSupportHost(DrmCursor* cursor) - : cursor_(cursor) { -} +namespace { + +// Helper class that provides DrmCursor with a mechanism to send messages +// to the GPU process. +class CursorIPC : public DrmCursorProxy { + public: + CursorIPC(scoped_refptr<base::SingleThreadTaskRunner> send_runner, + const base::Callback<void(IPC::Message*)>& send_callback); + ~CursorIPC() override; + + // DrmCursorProxy implementation. + 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; + + private: + bool IsConnected(); + void Send(IPC::Message* message); -DrmGpuPlatformSupportHost::~DrmGpuPlatformSupportHost() { + scoped_refptr<base::SingleThreadTaskRunner> send_runner_; + base::Callback<void(IPC::Message*)> send_callback_; + + DISALLOW_COPY_AND_ASSIGN(CursorIPC); +}; + +CursorIPC::CursorIPC(scoped_refptr<base::SingleThreadTaskRunner> send_runner, + const base::Callback<void(IPC::Message*)>& send_callback) + : send_runner_(send_runner), send_callback_(send_callback) {} + +CursorIPC::~CursorIPC() {} + +bool CursorIPC::IsConnected() { + return !send_callback_.is_null(); } -void DrmGpuPlatformSupportHost::RegisterHandler( - GpuPlatformSupportHost* handler) { - handlers_.push_back(handler); +void CursorIPC::CursorSet(gfx::AcceleratedWidget window, + const std::vector<SkBitmap>& bitmaps, + const gfx::Point& point, + int frame_delay_ms) { + Send(new OzoneGpuMsg_CursorSet(window, bitmaps, point, frame_delay_ms)); +} - if (IsConnected()) - handler->OnChannelEstablished(host_id_, send_runner_, send_callback_); +void CursorIPC::Move(gfx::AcceleratedWidget window, const gfx::Point& point) { + Send(new OzoneGpuMsg_CursorMove(window, point)); } -void DrmGpuPlatformSupportHost::UnregisterHandler( - GpuPlatformSupportHost* handler) { - std::vector<GpuPlatformSupportHost*>::iterator it = - std::find(handlers_.begin(), handlers_.end(), handler); - if (it != handlers_.end()) - handlers_.erase(it); +void CursorIPC::Send(IPC::Message* message) { + if (IsConnected() && + send_runner_->PostTask(FROM_HERE, base::Bind(send_callback_, message))) + return; + + // Drop disconnected updates. DrmWindowHost will call + // CommitBoundsChange() when we connect to initialize the cursor + // location. + delete message; } -void DrmGpuPlatformSupportHost::AddChannelObserver(ChannelObserver* observer) { - channel_observers_.AddObserver(observer); +} // namespace + +DrmGpuPlatformSupportHost::DrmGpuPlatformSupportHost(DrmCursor* cursor) + : cursor_(cursor) {} + +DrmGpuPlatformSupportHost::~DrmGpuPlatformSupportHost() {} + +void DrmGpuPlatformSupportHost::AddGpuThreadObserver( + GpuThreadObserver* observer) { + gpu_thread_observers_.AddObserver(observer); if (IsConnected()) - observer->OnChannelEstablished(); + observer->OnGpuThreadReady(); } -void DrmGpuPlatformSupportHost::RemoveChannelObserver( - ChannelObserver* observer) { - channel_observers_.RemoveObserver(observer); +void DrmGpuPlatformSupportHost::RemoveGpuThreadObserver( + GpuThreadObserver* observer) { + gpu_thread_observers_.RemoveObserver(observer); } bool DrmGpuPlatformSupportHost::IsConnected() { @@ -63,41 +110,37 @@ void DrmGpuPlatformSupportHost::OnChannelEstablished( send_runner_ = send_runner; send_callback_ = send_callback; - for (size_t i = 0; i < handlers_.size(); ++i) - handlers_[i]->OnChannelEstablished(host_id, send_runner_, send_callback_); - - FOR_EACH_OBSERVER(ChannelObserver, channel_observers_, - OnChannelEstablished()); + FOR_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, + 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 such that the // (windowing) state on the GPU can be initialized before the cursor is // allowed to IPC messages (which are targeted to a specific window). - cursor_->OnChannelEstablished(host_id, send_runner_, send_callback_); + cursor_->SetDrmCursorProxy(new CursorIPC(send_runner_, send_callback_)); } void DrmGpuPlatformSupportHost::OnChannelDestroyed(int host_id) { TRACE_EVENT1("drm", "DrmGpuPlatformSupportHost::OnChannelDestroyed", "host_id", host_id); - cursor_->OnChannelDestroyed(host_id); if (host_id_ == host_id) { + cursor_->ResetDrmCursorProxy(); host_id_ = -1; send_runner_ = nullptr; send_callback_.Reset(); - FOR_EACH_OBSERVER(ChannelObserver, channel_observers_, - OnChannelDestroyed()); + FOR_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, + OnGpuThreadRetired()); } - for (size_t i = 0; i < handlers_.size(); ++i) - handlers_[i]->OnChannelDestroyed(host_id); } bool DrmGpuPlatformSupportHost::OnMessageReceived(const IPC::Message& message) { - for (size_t i = 0; i < handlers_.size(); ++i) - if (handlers_[i]->OnMessageReceived(message)) - return true; + if (OnMessageReceivedForDrmDisplayHostManager(message)) + return true; + if (OnMessageReceivedForDrmOverlayManager(message)) + return true; return false; } @@ -111,4 +154,164 @@ bool DrmGpuPlatformSupportHost::Send(IPC::Message* message) { return false; } +// DisplayHost +void DrmGpuPlatformSupportHost::RegisterHandlerForDrmDisplayHostManager( + DrmDisplayHostManager* handler) { + display_manager_ = handler; +} + +void DrmGpuPlatformSupportHost::UnRegisterHandlerForDrmDisplayHostManager() { + display_manager_ = nullptr; +} + +bool DrmGpuPlatformSupportHost::OnMessageReceivedForDrmDisplayHostManager( + const IPC::Message& message) { + bool handled = true; + + IPC_BEGIN_MESSAGE_MAP(DrmGpuPlatformSupportHost, message) + IPC_MESSAGE_HANDLER(OzoneHostMsg_UpdateNativeDisplays, + OnUpdateNativeDisplays) + IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayConfigured, OnDisplayConfigured) + IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateReceived, OnHDCPStateReceived) + IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateUpdated, OnHDCPStateUpdated) + IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlTaken, OnTakeDisplayControl) + IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlRelinquished, + OnRelinquishDisplayControl) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void DrmGpuPlatformSupportHost::OnUpdateNativeDisplays( + const std::vector<DisplaySnapshot_Params>& params) { + display_manager_->GpuHasUpdatedNativeDisplays(params); +} + +void DrmGpuPlatformSupportHost::OnDisplayConfigured(int64_t display_id, + bool status) { + display_manager_->GpuConfiguredDisplay(display_id, status); +} + +void DrmGpuPlatformSupportHost::OnHDCPStateReceived(int64_t display_id, + bool status, + HDCPState state) { + display_manager_->GpuReceivedHDCPState(display_id, status, state); +} + +void DrmGpuPlatformSupportHost::OnHDCPStateUpdated(int64_t display_id, + bool status) { + display_manager_->GpuUpdatedHDCPState(display_id, status); +} + +void DrmGpuPlatformSupportHost::OnTakeDisplayControl(bool status) { + display_manager_->GpuTookDisplayControl(status); +} + +void DrmGpuPlatformSupportHost::OnRelinquishDisplayControl(bool status) { + display_manager_->GpuRelinquishedDisplayControl(status); +} + +bool DrmGpuPlatformSupportHost::GpuRefreshNativeDisplays() { + return Send(new OzoneGpuMsg_RefreshNativeDisplays()); +} + +bool DrmGpuPlatformSupportHost::GpuTakeDisplayControl() { + return Send(new OzoneGpuMsg_TakeDisplayControl()); +} + +bool DrmGpuPlatformSupportHost::GpuRelinquishDisplayControl() { + return Send(new OzoneGpuMsg_RelinquishDisplayControl()); +} + +bool DrmGpuPlatformSupportHost::GpuAddGraphicsDevice( + const base::FilePath& path, + const base::FileDescriptor& fd) { + return Send(new OzoneGpuMsg_AddGraphicsDevice(path, fd)); +} + +bool DrmGpuPlatformSupportHost::GpuRemoveGraphicsDevice( + const base::FilePath& path) { + return Send(new OzoneGpuMsg_RemoveGraphicsDevice(path)); +} + +// Overlays +void DrmGpuPlatformSupportHost::RegisterHandlerForDrmOverlayManager( + DrmOverlayManager* handler) { + overlay_manager_ = handler; +} + +void DrmGpuPlatformSupportHost::UnRegisterHandlerForDrmOverlayManager() { + overlay_manager_ = nullptr; +} + +bool DrmGpuPlatformSupportHost::OnMessageReceivedForDrmOverlayManager( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(DrmGpuPlatformSupportHost, message) + IPC_MESSAGE_HANDLER(OzoneHostMsg_OverlayCapabilitiesReceived, + OnOverlayResult) + // TODO(rjk): insert the extra + IPC_END_MESSAGE_MAP() + return handled; +} + +void DrmGpuPlatformSupportHost::OnOverlayResult( + gfx::AcceleratedWidget widget, + const std::vector<OverlayCheck_Params>& params) { + overlay_manager_->GpuSentOverlayResult(widget, params); +} + +bool DrmGpuPlatformSupportHost::GpuCheckOverlayCapabilities( + gfx::AcceleratedWidget widget, + const std::vector<OverlayCheck_Params>& new_params) { + return Send(new OzoneGpuMsg_CheckOverlayCapabilities(widget, new_params)); +} + +// DrmDisplayHost +bool DrmGpuPlatformSupportHost::GpuConfigureNativeDisplay( + int64_t display_id, + const ui::DisplayMode_Params& display_mode, + const gfx::Point& point) { + return Send( + new OzoneGpuMsg_ConfigureNativeDisplay(display_id, display_mode, point)); +} + +bool DrmGpuPlatformSupportHost::GpuDisableNativeDisplay(int64_t display_id) { + return Send(new OzoneGpuMsg_DisableNativeDisplay(display_id)); +} + +bool DrmGpuPlatformSupportHost::GpuGetHDCPState(int64_t display_id) { + return Send(new OzoneGpuMsg_GetHDCPState(display_id)); +} + +bool DrmGpuPlatformSupportHost::GpuSetHDCPState(int64_t display_id, + ui::HDCPState state) { + return Send(new OzoneGpuMsg_SetHDCPState(display_id, state)); +} + +bool DrmGpuPlatformSupportHost::GpuSetColorCorrection( + int64_t display_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + return Send(new OzoneGpuMsg_SetColorCorrection(display_id, degamma_lut, + gamma_lut, correction_matrix)); +} + +bool DrmGpuPlatformSupportHost::GpuDestroyWindow( + gfx::AcceleratedWidget widget) { + return Send(new OzoneGpuMsg_DestroyWindow(widget)); +} + +bool DrmGpuPlatformSupportHost::GpuCreateWindow(gfx::AcceleratedWidget widget) { + return Send(new OzoneGpuMsg_CreateWindow(widget)); +} + +bool DrmGpuPlatformSupportHost::GpuWindowBoundsChanged( + gfx::AcceleratedWidget widget, + const gfx::Rect& bounds) { + return Send(new OzoneGpuMsg_WindowBoundsChanged(widget, bounds)); +} + } // namespace ui 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 7961fd1b7c7..2cc4a176f43 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 @@ -9,7 +9,10 @@ #include "base/callback.h" #include "base/observer_list.h" +#include "ui/display/types/display_constants.h" #include "ui/gfx/native_widget_types.h" +#include "ui/ozone/common/gpu/ozone_gpu_message_params.h" +#include "ui/ozone/platform/drm/host/gpu_thread_adapter.h" #include "ui/ozone/public/gpu_platform_support_host.h" class SkBitmap; @@ -20,23 +23,18 @@ class Point; namespace ui { -class ChannelObserver; class DrmCursor; +class DrmDisplayHostMananger; +class DrmOverlayManager; +class GpuThreadObserver; class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost, + public GpuThreadAdapter, public IPC::Sender { public: DrmGpuPlatformSupportHost(DrmCursor* cursor); ~DrmGpuPlatformSupportHost() override; - void RegisterHandler(GpuPlatformSupportHost* handler); - void UnregisterHandler(GpuPlatformSupportHost* handler); - - void AddChannelObserver(ChannelObserver* observer); - void RemoveChannelObserver(ChannelObserver* observer); - - bool IsConnected(); - // GpuPlatformSupportHost: void OnChannelEstablished( int host_id, @@ -50,15 +48,77 @@ class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost, // IPC::Sender: bool Send(IPC::Message* message) override; + // GpuThreadAdapter. + // Core functionality. + void AddGpuThreadObserver(GpuThreadObserver* observer) override; + void RemoveGpuThreadObserver(GpuThreadObserver* observer) override; + bool IsConnected() override; + + // Services needed for DrmDisplayHostMananger. + void RegisterHandlerForDrmDisplayHostManager( + DrmDisplayHostManager* handler) override; + void UnRegisterHandlerForDrmDisplayHostManager() override; + + bool GpuTakeDisplayControl() override; + bool GpuRefreshNativeDisplays() override; + bool GpuRelinquishDisplayControl() override; + bool GpuAddGraphicsDevice(const base::FilePath& path, + const base::FileDescriptor& fd) override; + bool GpuRemoveGraphicsDevice(const base::FilePath& path) override; + + // Methods needed for DrmOverlayManager. + // Methods for DrmOverlayManager. + void RegisterHandlerForDrmOverlayManager(DrmOverlayManager* handler) override; + void UnRegisterHandlerForDrmOverlayManager() override; + + // Services needed by DrmOverlayManager + bool GpuCheckOverlayCapabilities( + gfx::AcceleratedWidget widget, + const std::vector<OverlayCheck_Params>& new_params) override; + + // Services needed by DrmDisplayHost + bool GpuConfigureNativeDisplay(int64_t display_id, + const ui::DisplayMode_Params& display_mode, + const gfx::Point& point) override; + bool GpuDisableNativeDisplay(int64_t display_id) override; + bool GpuGetHDCPState(int64_t display_id) override; + bool GpuSetHDCPState(int64_t display_id, ui::HDCPState state) override; + bool GpuSetColorCorrection( + int64_t display_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) override; + + // Services needed by DrmWindowHost + bool GpuDestroyWindow(gfx::AcceleratedWidget widget) override; + bool GpuCreateWindow(gfx::AcceleratedWidget widget) override; + bool GpuWindowBoundsChanged(gfx::AcceleratedWidget widget, + const gfx::Rect& bounds) override; + private: + bool OnMessageReceivedForDrmDisplayHostManager(const IPC::Message& message); + void OnUpdateNativeDisplays( + const std::vector<DisplaySnapshot_Params>& displays); + void OnDisplayConfigured(int64_t display_id, bool status); + void OnHDCPStateReceived(int64_t display_id, bool status, HDCPState state); + void OnHDCPStateUpdated(int64_t display_id, bool status); + void OnTakeDisplayControl(bool status); + void OnRelinquishDisplayControl(bool status); + + bool OnMessageReceivedForDrmOverlayManager(const IPC::Message& message); + void OnOverlayResult(gfx::AcceleratedWidget widget, + const std::vector<OverlayCheck_Params>& params); + int host_id_ = -1; scoped_refptr<base::SingleThreadTaskRunner> send_runner_; base::Callback<void(IPC::Message*)> send_callback_; - std::vector<GpuPlatformSupportHost*> handlers_; // Not owned. + DrmDisplayHostManager* display_manager_; // Not owned. + DrmOverlayManager* overlay_manager_; // Not owned. + DrmCursor* cursor_; // Not owned. - base::ObserverList<ChannelObserver> channel_observers_; + base::ObserverList<GpuThreadObserver> gpu_thread_observers_; }; } // namespace ui 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 0d6389bce8c..1563516b107 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 @@ -102,11 +102,13 @@ bool DrmNativeDisplayDelegate::SetColorCalibrationProfile( return false; } -bool DrmNativeDisplayDelegate::SetGammaRamp( +bool DrmNativeDisplayDelegate::SetColorCorrection( const ui::DisplaySnapshot& output, - const std::vector<GammaRampRGBEntry>& lut) { + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { DrmDisplayHost* display = display_manager_->GetDisplay(output.display_id()); - display->SetGammaRamp(lut); + display->SetColorCorrection(degamma_lut, gamma_lut, correction_matrix); return true; } 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 f62514e1a3a..8141ce710ef 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 @@ -50,8 +50,10 @@ class DrmNativeDisplayDelegate : public NativeDisplayDelegate { bool SetColorCalibrationProfile( const ui::DisplaySnapshot& output, ui::ColorCalibrationProfile new_profile) override; - bool SetGammaRamp(const ui::DisplaySnapshot& output, - const std::vector<GammaRampRGBEntry>& lut) override; + bool SetColorCorrection(const ui::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; diff --git a/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.cc b/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.cc index 3cba7b75d20..a04330d7db2 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.cc @@ -1,4 +1,4 @@ -// Copyright 2015 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. @@ -10,159 +10,23 @@ #include "ui/gfx/geometry/rect_conversions.h" #include "ui/ozone/common/gpu/ozone_gpu_messages.h" -#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h" +#include "ui/ozone/platform/drm/host/drm_overlay_manager.h" #include "ui/ozone/platform/drm/host/drm_window_host.h" +#include "ui/ozone/public/overlay_manager_ozone.h" namespace ui { -namespace { -const size_t kMaxCacheSize = 200; -} // namespace - DrmOverlayCandidatesHost::DrmOverlayCandidatesHost( - DrmGpuPlatformSupportHost* platform_support, - DrmWindowHost* window) - : platform_support_(platform_support), - window_(window), - cache_(kMaxCacheSize) { - platform_support_->RegisterHandler(this); - window_->SetOverlayCandidatesHost(this); -} + DrmOverlayManager* manager, + gfx::AcceleratedWidget widget) + : overlay_manager_(manager), widget_(widget) {} -DrmOverlayCandidatesHost::~DrmOverlayCandidatesHost() { - platform_support_->UnregisterHandler(this); - window_->SetOverlayCandidatesHost(nullptr); -} +DrmOverlayCandidatesHost::~DrmOverlayCandidatesHost() {} void DrmOverlayCandidatesHost::CheckOverlaySupport( OverlaySurfaceCandidateList* candidates) { - std::vector<OverlayCheck_Params> overlay_params; - for (auto& candidate : *candidates) { - // Compositor doesn't have information about the total size of primary - // candidate. We get this information from display rect. - if (candidate.plane_z_order == 0) - candidate.buffer_size = gfx::ToNearestRect(candidate.display_rect).size(); - - overlay_params.push_back(OverlayCheck_Params(candidate)); - } - - const auto& iter = cache_.Get(overlay_params); - // We are still waiting on results for this candidate list from GPU. - if (iter != cache_.end() && iter->second) - return; - - size_t size = candidates->size(); - - if (iter == cache_.end()) { - // We can skip GPU side validation in case all candidates are invalid - // or we are checking only for Primary. - bool needs_gpu_validation = false; - for (size_t i = 0; i < size; i++) { - const OverlaySurfaceCandidate& candidate = candidates->at(i); - - if (candidate.plane_z_order == 0) { - // We expect primary to be always valid. - overlay_params.at(i).is_overlay_candidate = true; - continue; - } - - if (!CanHandleCandidate(candidate)) { - overlay_params.at(i).is_overlay_candidate = false; - continue; - } - - needs_gpu_validation = true; - } - - cache_.Put(overlay_params, needs_gpu_validation); - - if (needs_gpu_validation) - SendOverlayValidationRequest(overlay_params); - } else { - const std::vector<OverlayCheck_Params>& validated_params = iter->first; - DCHECK(size == validated_params.size()); - - for (size_t i = 0; i < size; i++) { - candidates->at(i).overlay_handled = - validated_params.at(i).is_overlay_candidate; - } - } -} - -void DrmOverlayCandidatesHost::OnChannelEstablished( - int host_id, - scoped_refptr<base::SingleThreadTaskRunner> send_runner, - const base::Callback<void(IPC::Message*)>& sender) { - // Reset any old cache. - ResetCache(); -} - -void DrmOverlayCandidatesHost::OnChannelDestroyed(int host_id) { + overlay_manager_->CheckOverlaySupport(candidates, widget_); } -bool DrmOverlayCandidatesHost::OnMessageReceived(const IPC::Message& message) { - bool handled = false; - IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(DrmOverlayCandidatesHost, message, &handled) - IPC_MESSAGE_FORWARD(OzoneHostMsg_OverlayCapabilitiesReceived, this, - DrmOverlayCandidatesHost::OnOverlayResult) - IPC_END_MESSAGE_MAP() - return handled; -} - -void DrmOverlayCandidatesHost::ResetCache() { - cache_.Clear(); -} - -void DrmOverlayCandidatesHost::SendOverlayValidationRequest( - const std::vector<OverlayCheck_Params>& new_params) const { - if (!platform_support_->IsConnected()) - return; - - platform_support_->Send(new OzoneGpuMsg_CheckOverlayCapabilities( - window_->GetAcceleratedWidget(), new_params)); -} - -void DrmOverlayCandidatesHost::OnOverlayResult( - bool* handled, - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& params) { - if (widget != window_->GetAcceleratedWidget()) - return; - - *handled = true; - cache_.Put(params, false); -} - -bool DrmOverlayCandidatesHost::CanHandleCandidate( - const OverlaySurfaceCandidate& candidate) const { - if (candidate.buffer_size.IsEmpty()) - return false; - - // 0.01 constant chosen to match DCHECKs in gfx::ToNearestRect and avoid - // that code asserting on quads that we accept. - if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f)) - return false; - - if (candidate.transform == gfx::OVERLAY_TRANSFORM_INVALID) - return false; - - if (candidate.plane_z_order != 0) { - // It is possible that the cc rect we get actually falls off the edge of - // the screen. Usually this is prevented via things like status bars - // blocking overlaying or cc clipping it, but in case it wasn't properly - // clipped (since GL will render this situation fine) just ignore it - // here. This should be an extremely rare occurrance. - if (!window_->GetBounds().Contains( - gfx::ToNearestRect(candidate.display_rect))) { - return false; - } - } - - if (candidate.is_clipped && - !candidate.clip_rect.Contains(candidate.quad_rect_in_target_space)) - return false; - - return true; -} } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h b/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h index 1d1feb6daaa..2797c2b25a5 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h @@ -1,9 +1,9 @@ -// Copyright 2015 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. -#ifndef UI_OZONE_PLATFORM_DRM_HOST_OVERLAY_CANDIDATES_H_ -#define UI_OZONE_PLATFORM_DRM_HOST_OVERLAY_CANDIDATES_H_ +#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_CANDIDATES_HOST_H_ +#define UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_CANDIDATES_HOST_H_ #include <stdint.h> @@ -13,15 +13,13 @@ #include "base/containers/mru_cache.h" #include "base/macros.h" -#include "base/memory/scoped_vector.h" -#include "ui/ozone/common/gpu/ozone_gpu_message_params.h" -#include "ui/ozone/public/gpu_platform_support_host.h" +#include "ui/ozone/platform/drm/host/gpu_thread_adapter.h" +#include "ui/ozone/platform/drm/host/gpu_thread_observer.h" #include "ui/ozone/public/overlay_candidates_ozone.h" namespace ui { -class DrmGpuPlatformSupportHost; -class DrmWindowHost; +class DrmOverlayManager; // This is an implementation of OverlayCandidatesOzone where the driver is asked // about overlay capabilities via IPC. We have no way of querying abstract @@ -32,50 +30,23 @@ class DrmWindowHost; // the result is returned in OzoneHostMsg_OverlayCapabilitiesReceived. Testing // is asynchronous, until the reply arrives that configuration will be failed. // -// There is a many:1 relationship between this class and -// DrmGpuPlatformSupportHost, each compositor will own one of these objects. -// Each request has a unique request ID, which is assigned from a shared -// sequence number so that replies can be routed to the correct object. -class DrmOverlayCandidatesHost : public OverlayCandidatesOzone, - public GpuPlatformSupportHost { +// All OverlayCandidatesOzone objects share a single cache of tested +// configurations stored in the overlay manager. +class DrmOverlayCandidatesHost : public OverlayCandidatesOzone { public: - DrmOverlayCandidatesHost(DrmGpuPlatformSupportHost* platform_support, - DrmWindowHost* window); + DrmOverlayCandidatesHost(DrmOverlayManager* manager_core, + gfx::AcceleratedWidget widget); ~DrmOverlayCandidatesHost() override; - // OverlayCandidatesOzone: void CheckOverlaySupport(OverlaySurfaceCandidateList* candidates) override; - // GpuPlatformSupportHost: - void OnChannelEstablished( - int host_id, - scoped_refptr<base::SingleThreadTaskRunner> send_runner, - const base::Callback<void(IPC::Message*)>& sender) override; - void OnChannelDestroyed(int host_id) override; - bool OnMessageReceived(const IPC::Message& message) override; - - void ResetCache(); - private: - void SendOverlayValidationRequest( - const std::vector<OverlayCheck_Params>& list) const; - void OnOverlayResult(bool* handled, - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& params); - bool CanHandleCandidate(const OverlaySurfaceCandidate& candidate) const; - - DrmGpuPlatformSupportHost* platform_support_; // Not owned. - DrmWindowHost* window_; // Not owned. - - // List of all OverlayCheck_Params which have been validated in GPU side. - // Value is set to true if we are waiting for validation results from GPU. - base::MRUCacheBase<std::vector<OverlayCheck_Params>, - bool, - base::MRUCacheNullDeletor<bool>> cache_; + DrmOverlayManager* overlay_manager_; // Not owned. + gfx::AcceleratedWidget widget_; DISALLOW_COPY_AND_ASSIGN(DrmOverlayCandidatesHost); }; } // namespace ui -#endif // UI_OZONE_PLATFORM_DRM_HOST_OVERLAY_CANDIDATES_H_ +#endif // UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_CANDIDATES_HOST_H_ 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 ae0a755e3d5..9ba5746f75a 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc @@ -1,37 +1,155 @@ -// Copyright 2015 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. #include "ui/ozone/platform/drm/host/drm_overlay_manager.h" +#include <stddef.h> + +#include <algorithm> + #include "base/command_line.h" #include "ui/gfx/geometry/rect_conversions.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" -#include "ui/ozone/public/overlay_candidates_ozone.h" #include "ui/ozone/public/ozone_switches.h" namespace ui { -DrmOverlayManager::DrmOverlayManager( - DrmGpuPlatformSupportHost* platform_support_host, - DrmWindowHostManager* manager) - : platform_support_host_(platform_support_host), window_manager_(manager) { +typedef OverlayCandidatesOzone::OverlaySurfaceCandidateList + OverlaySurfaceCandidateList; +typedef OverlayCandidatesOzone::OverlaySurfaceCandidate OverlaySurfaceCandidate; + +namespace { +const size_t kMaxCacheSize = 200; +} // namespace + +DrmOverlayManager::DrmOverlayManager(GpuThreadAdapter* proxy, + DrmWindowHostManager* window_manager) + : proxy_(proxy), window_manager_(window_manager), cache_(kMaxCacheSize) { is_supported_ = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kOzoneTestSingleOverlaySupport); + proxy_->RegisterHandlerForDrmOverlayManager(this); } DrmOverlayManager::~DrmOverlayManager() { + proxy_->UnRegisterHandlerForDrmOverlayManager(); } scoped_ptr<OverlayCandidatesOzone> DrmOverlayManager::CreateOverlayCandidates( gfx::AcceleratedWidget w) { if (!is_supported_) return nullptr; - DrmWindowHost* window = window_manager_->GetWindow(w); - DCHECK(window); - return make_scoped_ptr( - new DrmOverlayCandidatesHost(platform_support_host_, window)); + return make_scoped_ptr(new DrmOverlayCandidatesHost(this, w)); +} + +void DrmOverlayManager::CheckOverlaySupport( + OverlayCandidatesOzone::OverlaySurfaceCandidateList* candidates, + gfx::AcceleratedWidget widget) { + std::vector<OverlayCheck_Params> overlay_params; + 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; + continue; + } + + // Compositor doesn't have information about the total size of primary + // candidate. We get this information from display rect. + if (candidate.plane_z_order == 0) + candidate.buffer_size = gfx::ToNearestRect(candidate.display_rect).size(); + + overlay_params.push_back(OverlayCheck_Params(candidate)); + } + + const auto& iter = cache_.Get(overlay_params); + // We are still waiting on results for this candidate list from GPU. + if (iter != cache_.end() && iter->second) + return; + + size_t size = candidates->size(); + + if (iter == cache_.end()) { + // We can skip GPU side validation in case all candidates are invalid. + bool needs_gpu_validation = false; + for (size_t i = 0; i < size; i++) { + if (!overlay_params.at(i).is_overlay_candidate) + continue; + + const OverlaySurfaceCandidate& candidate = candidates->at(i); + if (!CanHandleCandidate(candidate, widget)) { + DCHECK(candidate.plane_z_order != 0); + overlay_params.at(i).is_overlay_candidate = false; + continue; + } + + needs_gpu_validation = true; + } + + cache_.Put(overlay_params, needs_gpu_validation); + + if (needs_gpu_validation) + SendOverlayValidationRequest(overlay_params, widget); + } else { + const std::vector<OverlayCheck_Params>& validated_params = iter->first; + DCHECK(size == validated_params.size()); + + for (size_t i = 0; i < size; i++) { + candidates->at(i).overlay_handled = + validated_params.at(i).is_overlay_candidate; + } + } +} + +void DrmOverlayManager::ResetCache() { + cache_.Clear(); +} + +void DrmOverlayManager::SendOverlayValidationRequest( + const std::vector<OverlayCheck_Params>& new_params, + gfx::AcceleratedWidget widget) const { + if (!proxy_->IsConnected()) + return; + + proxy_->GpuCheckOverlayCapabilities(widget, new_params); +} + +void DrmOverlayManager::GpuSentOverlayResult( + gfx::AcceleratedWidget widget, + const std::vector<OverlayCheck_Params>& params) { + cache_.Put(params, false); +} + +bool DrmOverlayManager::CanHandleCandidate( + const OverlaySurfaceCandidate& candidate, + gfx::AcceleratedWidget widget) const { + if (candidate.buffer_size.IsEmpty()) + return false; + + if (candidate.transform == gfx::OVERLAY_TRANSFORM_INVALID) + return false; + + if (candidate.plane_z_order != 0) { + // It is possible that the cc rect we get actually falls off the edge of + // the screen. Usually this is prevented via things like status bars + // blocking overlaying or cc clipping it, but in case it wasn't properly + // clipped (since GL will render this situation fine) just ignore it + // here. This should be an extremely rare occurrance. + DrmWindowHost* window = window_manager_->GetWindow(widget); + if (!window->GetBounds().Contains( + gfx::ToNearestRect(candidate.display_rect))) { + return false; + } + } + + if (candidate.is_clipped && + !candidate.clip_rect.Contains(candidate.quad_rect_in_target_space)) + return false; + + return true; } } // namespace ui 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 b52a9b647a7..6c364d0e75c 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h @@ -1,32 +1,61 @@ -// Copyright 2015 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. #ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_MANAGER_H_ #define UI_OZONE_PLATFORM_DRM_HOST_DRM_OVERLAY_MANAGER_H_ +#include <stdint.h> + +#include <vector> + +#include "base/containers/mru_cache.h" #include "base/macros.h" +#include "base/memory/scoped_vector.h" +#include "ui/ozone/platform/drm/host/gpu_thread_adapter.h" +#include "ui/ozone/public/overlay_candidates_ozone.h" #include "ui/ozone/public/overlay_manager_ozone.h" namespace ui { - -class DrmGpuPlatformSupportHost; class DrmWindowHostManager; class DrmOverlayManager : public OverlayManagerOzone { public: - DrmOverlayManager(DrmGpuPlatformSupportHost* platform_support_host, - DrmWindowHostManager* manager); + DrmOverlayManager(GpuThreadAdapter* proxy, + DrmWindowHostManager* window_manager); ~DrmOverlayManager() override; // OverlayManagerOzone: scoped_ptr<OverlayCandidatesOzone> CreateOverlayCandidates( gfx::AcceleratedWidget w) override; + void ResetCache(); + + // 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); + + // Service method for DrmOverlayCandidatesHost + void CheckOverlaySupport( + OverlayCandidatesOzone::OverlaySurfaceCandidateList* candidates, + gfx::AcceleratedWidget widget); + private: - DrmGpuPlatformSupportHost* platform_support_host_; - DrmWindowHostManager* window_manager_; + void SendOverlayValidationRequest( + const std::vector<OverlayCheck_Params>& new_params, + gfx::AcceleratedWidget widget) const; + bool CanHandleCandidate( + const OverlayCandidatesOzone::OverlaySurfaceCandidate& candidate, + gfx::AcceleratedWidget widget) const; + bool is_supported_; + GpuThreadAdapter* proxy_; // Not owned. + DrmWindowHostManager* window_manager_; // Not owned. + + // List of all OverlayCheck_Params which have been validated in GPU side. + // Value is set to true if we are waiting for validation results from GPU. + base::MRUCache<std::vector<OverlayCheck_Params>, bool> cache_; DISALLOW_COPY_AND_ASSIGN(DrmOverlayManager); }; diff --git a/chromium/ui/ozone/platform/drm/host/drm_window_host.cc b/chromium/ui/ozone/platform/drm/host/drm_window_host.cc index 4255904d35a..56b59bbb905 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_window_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_window_host.cc @@ -11,12 +11,10 @@ #include "ui/events/ozone/events_ozone.h" #include "ui/events/platform/platform_event_source.h" #include "ui/gfx/display.h" -#include "ui/ozone/common/gpu/ozone_gpu_messages.h" #include "ui/ozone/platform/drm/host/drm_cursor.h" #include "ui/ozone/platform/drm/host/drm_display_host.h" #include "ui/ozone/platform/drm/host/drm_display_host_manager.h" -#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h" -#include "ui/ozone/platform/drm/host/drm_overlay_candidates_host.h" +#include "ui/ozone/platform/drm/host/drm_overlay_manager.h" #include "ui/ozone/platform/drm/host/drm_window_host_manager.h" #include "ui/platform_window/platform_window_delegate.h" @@ -24,18 +22,19 @@ namespace ui { DrmWindowHost::DrmWindowHost(PlatformWindowDelegate* delegate, const gfx::Rect& bounds, - DrmGpuPlatformSupportHost* sender, + GpuThreadAdapter* sender, EventFactoryEvdev* event_factory, DrmCursor* cursor, DrmWindowHostManager* window_manager, - DrmDisplayHostManager* display_manager) + DrmDisplayHostManager* display_manager, + DrmOverlayManager* overlay_manager) : delegate_(delegate), sender_(sender), event_factory_(event_factory), cursor_(cursor), window_manager_(window_manager), display_manager_(display_manager), - overlay_candidates_host_(nullptr), + overlay_manager_(overlay_manager), bounds_(bounds), widget_(window_manager->NextAcceleratedWidget()) { window_manager_->AddWindow(widget_, this); @@ -46,12 +45,12 @@ DrmWindowHost::~DrmWindowHost() { window_manager_->RemoveWindow(widget_); cursor_->OnWindowRemoved(widget_); - sender_->RemoveChannelObserver(this); - sender_->Send(new OzoneGpuMsg_DestroyWindow(widget_)); + sender_->RemoveGpuThreadObserver(this); + sender_->GpuDestroyWindow(widget_); } void DrmWindowHost::Initialize() { - sender_->AddChannelObserver(this); + sender_->AddGpuThreadObserver(this); PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); cursor_->OnWindowAdded(widget_, bounds_, GetCursorConfinedBounds()); delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f); @@ -187,26 +186,20 @@ uint32_t DrmWindowHost::DispatchEvent(const PlatformEvent& native_event) { return POST_DISPATCH_STOP_PROPAGATION; } -void DrmWindowHost::OnChannelEstablished() { - sender_->Send(new OzoneGpuMsg_CreateWindow(widget_)); +void DrmWindowHost::OnGpuThreadReady() { + sender_->GpuCreateWindow(widget_); SendBoundsChange(); } -void DrmWindowHost::OnChannelDestroyed() { -} - -void DrmWindowHost::SetOverlayCandidatesHost(DrmOverlayCandidatesHost* host) { - overlay_candidates_host_ = host; -} +void DrmWindowHost::OnGpuThreadRetired() {} void DrmWindowHost::SendBoundsChange() { // Update the cursor before the window so that the cursor stays within the // window bounds when the window size shrinks. cursor_->CommitBoundsChange(widget_, bounds_, GetCursorConfinedBounds()); - sender_->Send(new OzoneGpuMsg_WindowBoundsChanged(widget_, bounds_)); + sender_->GpuWindowBoundsChanged(widget_, bounds_); - if (overlay_candidates_host_) - overlay_candidates_host_->ResetCache(); + overlay_manager_->ResetCache(); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/host/drm_window_host.h b/chromium/ui/ozone/platform/drm/host/drm_window_host.h index dabcfc9b459..a66729c2d8d 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_window_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_window_host.h @@ -13,18 +13,18 @@ #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" -#include "ui/ozone/platform/drm/host/channel_observer.h" +#include "ui/ozone/platform/drm/host/gpu_thread_observer.h" #include "ui/platform_window/platform_window.h" namespace ui { class DrmDisplayHostManager; class DrmCursor; -class DrmGpuPlatformSupportHost; -class DrmOverlayCandidatesHost; class DrmGpuWindow; +class DrmOverlayManager; class DrmWindowHostManager; class EventFactoryEvdev; +class GpuThreadAdapter; // Implementation of the platform window. This object and its handle |widget_| // uniquely identify a window. Since the DRI/GBM platform is split into 2 @@ -38,15 +38,16 @@ class EventFactoryEvdev; // associated with the window (the surface is created on the GPU process). class DrmWindowHost : public PlatformWindow, public PlatformEventDispatcher, - public ChannelObserver { + public GpuThreadObserver { public: DrmWindowHost(PlatformWindowDelegate* delegate, const gfx::Rect& bounds, - DrmGpuPlatformSupportHost* sender, + GpuThreadAdapter* sender, EventFactoryEvdev* event_factory, DrmCursor* cursor, DrmWindowHostManager* window_manager, - DrmDisplayHostManager* display_manager); + DrmDisplayHostManager* display_manager, + DrmOverlayManager* overlay_manager); ~DrmWindowHost() override; void Initialize(); @@ -77,22 +78,20 @@ class DrmWindowHost : public PlatformWindow, bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; - // ChannelObserver: - void OnChannelEstablished() override; - void OnChannelDestroyed() override; - - void SetOverlayCandidatesHost(DrmOverlayCandidatesHost* host); + // GpuThreadObserver: + void OnGpuThreadReady() override; + void OnGpuThreadRetired() override; private: void SendBoundsChange(); PlatformWindowDelegate* delegate_; // Not owned. - DrmGpuPlatformSupportHost* sender_; // Not owned. + GpuThreadAdapter* sender_; // Not owned. EventFactoryEvdev* event_factory_; // Not owned. DrmCursor* cursor_; // Not owned. DrmWindowHostManager* window_manager_; // Not owned. DrmDisplayHostManager* display_manager_; // Not owned. - DrmOverlayCandidatesHost* overlay_candidates_host_; // Not owned. + DrmOverlayManager* overlay_manager_; // Not owned. gfx::Rect bounds_; gfx::AcceleratedWidget widget_; diff --git a/chromium/ui/ozone/platform/drm/host/drm_window_host_manager.h b/chromium/ui/ozone/platform/drm/host/drm_window_host_manager.h index 3ae8e2db99b..af97c5c9662 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_window_host_manager.h +++ b/chromium/ui/ozone/platform/drm/host/drm_window_host_manager.h @@ -17,7 +17,6 @@ class Point; namespace ui { -class DrmGpuPlatformSupportHost; class DrmWindowHost; // Responsible for keeping the mapping between the allocated widgets and diff --git a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h new file mode 100644 index 00000000000..866dfd8c4a1 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h @@ -0,0 +1,76 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_DRM_HOST_GPU_THREAD_ADAPTER_H_ +#define UI_OZONE_PLATFORM_DRM_HOST_GPU_THREAD_ADAPTER_H_ + +#include "base/file_descriptor_posix.h" +#include "ui/display/types/display_constants.h" +#include "ui/display/types/gamma_ramp_rgb_entry.h" +#include "ui/ozone/common/gpu/ozone_gpu_message_params.h" +#include "ui/ozone/public/overlay_candidates_ozone.h" + +namespace ui { + +class DrmDisplayHostManager; +class DrmOverlayManager; +class GpuThreadObserver; + +// Provides the services that the various host components need +// to use either a GPU process or thread for their implementation. +class GpuThreadAdapter { + public: + virtual ~GpuThreadAdapter() {} + + virtual bool IsConnected() = 0; + virtual void AddGpuThreadObserver(GpuThreadObserver* observer) = 0; + virtual void RemoveGpuThreadObserver(GpuThreadObserver* observer) = 0; + + // Methods for Display management. + virtual void RegisterHandlerForDrmDisplayHostManager( + DrmDisplayHostManager* handler) = 0; + virtual void UnRegisterHandlerForDrmDisplayHostManager() = 0; + + // Services needed for DrmDisplayHostMananger + virtual bool GpuTakeDisplayControl() = 0; + virtual bool GpuRefreshNativeDisplays() = 0; + virtual bool GpuRelinquishDisplayControl() = 0; + virtual bool GpuAddGraphicsDevice(const base::FilePath& path, + const base::FileDescriptor& fd) = 0; + virtual bool GpuRemoveGraphicsDevice(const base::FilePath& path) = 0; + + // Methods for DrmOverlayManager. + virtual void RegisterHandlerForDrmOverlayManager( + DrmOverlayManager* handler) = 0; + virtual void UnRegisterHandlerForDrmOverlayManager() = 0; + + // Services needed by DrmOverlayManager + virtual bool GpuCheckOverlayCapabilities( + gfx::AcceleratedWidget widget, + const std::vector<OverlayCheck_Params>& new_params) = 0; + + // Services needed by DrmDisplayHost + virtual bool GpuConfigureNativeDisplay( + int64_t display_id, + const ui::DisplayMode_Params& display_mode, + const gfx::Point& point) = 0; + virtual bool GpuDisableNativeDisplay(int64_t display_id) = 0; + virtual bool GpuGetHDCPState(int64_t display_id) = 0; + virtual bool GpuSetHDCPState(int64_t display_id, ui::HDCPState state) = 0; + virtual bool GpuSetColorCorrection( + int64_t display_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) = 0; + + // Services needed by DrmWindowHost + virtual bool GpuDestroyWindow(gfx::AcceleratedWidget widget) = 0; + virtual bool GpuCreateWindow(gfx::AcceleratedWidget widget) = 0; + virtual bool GpuWindowBoundsChanged(gfx::AcceleratedWidget widget, + const gfx::Rect& bounds) = 0; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_DRM_HOST_GPU_THREAD_ADAPTER_H_ diff --git a/chromium/ui/ozone/platform/drm/host/gpu_thread_observer.h b/chromium/ui/ozone/platform/drm/host/gpu_thread_observer.h new file mode 100644 index 00000000000..3307dfa8fcb --- /dev/null +++ b/chromium/ui/ozone/platform/drm/host/gpu_thread_observer.h @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_DRM_HOST_GPU_THREAD_OBSERVER_H_ +#define UI_OZONE_PLATFORM_DRM_HOST_GPU_THREAD_OBSERVER_H_ + +namespace ui { + +// Observes the channel state. +class GpuThreadObserver { + public: + virtual ~GpuThreadObserver() {} + + // Called when a GPU thread implementation has become available. + virtual void OnGpuThreadReady() = 0; + // Called when the GPU thread implementation has ceased to be + // available. + virtual void OnGpuThreadRetired() = 0; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_DRM_HOST_GPU_THREAD_OBSERVER_H_ diff --git a/chromium/ui/ozone/platform/drm/mus_thread_proxy.cc b/chromium/ui/ozone/platform/drm/mus_thread_proxy.cc new file mode 100644 index 00000000000..1f3e0167e20 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/mus_thread_proxy.cc @@ -0,0 +1,339 @@ +// 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/thread_task_runner_handle.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 { + +MusThreadProxy::MusThreadProxy() + : ws_task_runner_(base::ThreadTaskRunnerHandle::Get()), + drm_thread_(nullptr), + weak_ptr_factory_(this) {} + +MusThreadProxy::~MusThreadProxy() { + DCHECK(on_window_server_thread_.CalledOnValidThread()); + FOR_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, + 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; +} + +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_EACH_OBSERVER(GpuThreadObserver, gpu_thread_observers_, + OnGpuThreadReady()); +} + +void MusThreadProxy::AddGpuThreadObserver(GpuThreadObserver* observer) { + DCHECK(on_window_server_thread_.CalledOnValidThread()); + + gpu_thread_observers_.AddObserver(observer); + if (IsConnected()) + 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(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + 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(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + 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(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, base::Bind(&DrmThread::SetWindowBounds, + base::Unretained(drm_thread_), widget, bounds)); + return true; +} + +void MusThreadProxy::CursorSet(gfx::AcceleratedWidget widget, + const std::vector<SkBitmap>& bitmaps, + const gfx::Point& location, + int frame_delay_ms) { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + 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. + DCHECK(drm_thread_->IsRunning()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, base::Bind(&DrmThread::MoveCursor, + base::Unretained(drm_thread_), widget, location)); +} + +// 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(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + auto callback = + base::Bind(&MusThreadProxy::GpuCheckOverlayCapabilitiesCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, base::Bind(&DrmThread::CheckOverlayCapabilities, + base::Unretained(drm_thread_), widget, overlays, + CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuRefreshNativeDisplays() { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + auto callback = base::Bind(&MusThreadProxy::GpuRefreshNativeDisplaysCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, + base::Bind(&DrmThread::RefreshNativeDisplays, + base::Unretained(drm_thread_), CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuConfigureNativeDisplay(int64_t id, + const DisplayMode_Params& mode, + const gfx::Point& origin) { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + + auto callback = base::Bind(&MusThreadProxy::GpuConfigureNativeDisplayCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, base::Bind(&DrmThread::ConfigureNativeDisplay, + base::Unretained(drm_thread_), id, mode, origin, + CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuDisableNativeDisplay(int64_t id) { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + auto callback = base::Bind(&MusThreadProxy::GpuDisableNativeDisplayCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, base::Bind(&DrmThread::DisableNativeDisplay, + base::Unretained(drm_thread_), id, + CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuTakeDisplayControl() { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + auto callback = base::Bind(&MusThreadProxy::GpuTakeDisplayControlCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, + base::Bind(&DrmThread::TakeDisplayControl, base::Unretained(drm_thread_), + CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuRelinquishDisplayControl() { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + auto callback = + base::Bind(&MusThreadProxy::GpuRelinquishDisplayControlCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, + base::Bind(&DrmThread::RelinquishDisplayControl, + base::Unretained(drm_thread_), CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuAddGraphicsDevice(const base::FilePath& path, + const base::FileDescriptor& fd) { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + 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(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + 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(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + auto callback = base::Bind(&MusThreadProxy::GpuGetHDCPStateCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, + base::Bind(&DrmThread::GetHDCPState, base::Unretained(drm_thread_), + display_id, CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuSetHDCPState(int64_t display_id, HDCPState state) { + DCHECK(on_window_server_thread_.CalledOnValidThread()); + DCHECK(drm_thread_->IsRunning()); + auto callback = base::Bind(&MusThreadProxy::GpuSetHDCPStateCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_thread_->task_runner()->PostTask( + FROM_HERE, + base::Bind(&DrmThread::SetHDCPState, base::Unretained(drm_thread_), + display_id, state, CreateSafeCallback(callback))); + return true; +} + +bool MusThreadProxy::GpuSetColorCorrection( + int64_t id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + DCHECK(drm_thread_->IsRunning()); + DCHECK(on_window_server_thread_.CalledOnValidThread()); + 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 { + DCHECK(on_window_server_thread_.CalledOnValidThread()); + overlay_manager_->GpuSentOverlayResult(widget, overlays); +} + +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( + const std::vector<DisplaySnapshot_Params>& displays) const { + DCHECK(on_window_server_thread_.CalledOnValidThread()); + display_manager_->GpuHasUpdatedNativeDisplays(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, + 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/mus_thread_proxy.h b/chromium/ui/ozone/platform/drm/mus_thread_proxy.h new file mode 100644 index 00000000000..e39eefcb409 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/mus_thread_proxy.h @@ -0,0 +1,141 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_ +#define UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_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 "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" + +namespace base { +class SingleThreadTaskRunner; +} + +namespace ui { + +class DrmCursor; +class DrmDisplayHostManager; +class DrmOverlayManager; +class DrmThread; +class GpuThreadObserver; + +// 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 { + public: + MusThreadProxy(); + ~MusThreadProxy() override; + + 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. + void AddGpuThreadObserver(GpuThreadObserver* observer) override; + void RemoveGpuThreadObserver(GpuThreadObserver* observer) override; + bool IsConnected() override; + + // Services needed for DrmDisplayHostMananger. + void RegisterHandlerForDrmDisplayHostManager( + DrmDisplayHostManager* handler) override; + void UnRegisterHandlerForDrmDisplayHostManager() override; + + bool GpuTakeDisplayControl() override; + bool GpuRefreshNativeDisplays() override; + bool GpuRelinquishDisplayControl() override; + bool GpuAddGraphicsDevice(const base::FilePath& path, + const base::FileDescriptor& 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; + + // Services needed by DrmDisplayHost + bool GpuConfigureNativeDisplay(int64_t display_id, + const ui::DisplayMode_Params& display_mode, + const gfx::Point& point) override; + bool GpuDisableNativeDisplay(int64_t display_id) override; + bool GpuGetHDCPState(int64_t display_id) override; + bool GpuSetHDCPState(int64_t display_id, ui::HDCPState state) override; + bool GpuSetColorCorrection( + int64_t display_id, + const std::vector<GammaRampRGBEntry>& degamma_lut, + const std::vector<GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) override; + + // Services needed by DrmWindowHost + bool GpuDestroyWindow(gfx::AcceleratedWidget widget) override; + bool GpuCreateWindow(gfx::AcceleratedWidget widget) override; + 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; + + private: + void RunObservers(); + void DispatchObserversFromDrmThread(); + + void GpuCheckOverlayCapabilitiesCallback( + gfx::AcceleratedWidget widget, + const std::vector<OverlayCheck_Params>& overlays) const; + + void GpuConfigureNativeDisplayCallback(int64_t display_id, + bool success) const; + + void GpuRefreshNativeDisplaysCallback( + const std::vector<DisplaySnapshot_Params>& displays) const; + void GpuDisableNativeDisplayCallback(int64_t display_id, bool success) const; + void GpuTakeDisplayControlCallback(bool success) const; + void GpuRelinquishDisplayControlCallback(bool success) const; + void GpuGetHDCPStateCallback(int64_t display_id, + bool success, + 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_; + + DrmDisplayHostManager* display_manager_; // Not owned. + DrmOverlayManager* overlay_manager_; // Not owned. + + base::ObserverList<GpuThreadObserver> gpu_thread_observers_; + + base::ThreadChecker on_window_server_thread_; + + base::WeakPtrFactory<MusThreadProxy> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(MusThreadProxy); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_ diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc index 3dbe176c0b9..9ccb57a70f9 100644 --- a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc +++ b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc @@ -9,9 +9,12 @@ #include <gbm.h> #include <stdlib.h> #include <xf86drm.h> + +#include <memory> #include <utility> #include "base/bind.h" +#include "base/command_line.h" #include "base/macros.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/events/ozone/device/device_manager.h" @@ -35,10 +38,11 @@ #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/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support.h" #include "ui/ozone/public/gpu_platform_support_host.h" -#include "ui/ozone/public/ozone_platform.h" // nogncheck +#include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/ozone_switches.h" #if defined(USE_XKBCOMMON) @@ -72,6 +76,13 @@ class GlApiLoader { DISALLOW_COPY_AND_ASSIGN(GlApiLoader); }; +// Returns true if we should operate in Mus mode. +bool RunningInsideMus() { + bool has_channel_handle = base::CommandLine::ForCurrentProcess()->HasSwitch( + "mojo-platform-channel-handle"); + return has_channel_handle; +} + class OzonePlatformGbm : public OzonePlatform { public: OzonePlatformGbm() {} @@ -102,10 +113,15 @@ class OzonePlatformGbm : public OzonePlatform { scoped_ptr<PlatformWindow> CreatePlatformWindow( PlatformWindowDelegate* delegate, const gfx::Rect& bounds) override { - scoped_ptr<DrmWindowHost> platform_window( - new DrmWindowHost(delegate, bounds, gpu_platform_support_host_.get(), - event_factory_ozone_.get(), cursor_.get(), - window_manager_.get(), display_manager_.get())); + GpuThreadAdapter* adapter = gpu_platform_support_host_.get(); + if (RunningInsideMus()) { + DCHECK(drm_thread_) << "drm_thread_ should exist (and be running) here."; + adapter = mus_thread_proxy_.get(); + } + + scoped_ptr<DrmWindowHost> platform_window(new DrmWindowHost( + delegate, bounds, adapter, event_factory_ozone_.get(), cursor_.get(), + window_manager_.get(), display_manager_.get(), overlay_manager_.get())); platform_window->Initialize(); return std::move(platform_window); } @@ -113,17 +129,6 @@ class OzonePlatformGbm : public OzonePlatform { return make_scoped_ptr( new DrmNativeDisplayDelegate(display_manager_.get())); } - base::ScopedFD OpenClientNativePixmapDevice() const override { -#if defined(USE_VGEM_MAP) - int vgem_fd = drmOpenWithType("vgem", nullptr, DRM_NODE_RENDER); - if (vgem_fd < 0) { - PLOG(ERROR) << "Failed to find vgem device"; - vgem_fd = -1; - } - return base::ScopedFD(vgem_fd); -#endif - return base::ScopedFD(); - } void InitializeUI() override { device_manager_ = CreateDeviceManager(); window_manager_.reset(new DrmWindowHostManager()); @@ -138,25 +143,54 @@ class OzonePlatformGbm : public OzonePlatform { event_factory_ozone_.reset(new EventFactoryEvdev( cursor_.get(), device_manager_.get(), KeyboardLayoutEngineManager::GetKeyboardLayoutEngine())); - gpu_platform_support_host_.reset( - new DrmGpuPlatformSupportHost(cursor_.get())); - display_manager_.reset(new DrmDisplayHostManager( - gpu_platform_support_host_.get(), device_manager_.get(), - event_factory_ozone_->input_controller())); + + GpuThreadAdapter* adapter; + if (RunningInsideMus()) { + gl_api_loader_.reset(new GlApiLoader()); + mus_thread_proxy_.reset(new MusThreadProxy()); + adapter = mus_thread_proxy_.get(); + cursor_->SetDrmCursorProxy(mus_thread_proxy_.get()); + } else { + gpu_platform_support_host_.reset( + new DrmGpuPlatformSupportHost(cursor_.get())); + adapter = gpu_platform_support_host_.get(); + } + + display_manager_.reset( + new DrmDisplayHostManager(adapter, device_manager_.get(), + event_factory_ozone_->input_controller())); cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone); - overlay_manager_.reset(new DrmOverlayManager( - gpu_platform_support_host_.get(), window_manager_.get())); + overlay_manager_.reset( + new DrmOverlayManager(adapter, window_manager_.get())); + + if (RunningInsideMus()) { + mus_thread_proxy_->ProvideManagers(display_manager_.get(), + overlay_manager_.get()); + } } void InitializeGPU() override { - gl_api_loader_.reset(new GlApiLoader()); + InterThreadMessagingProxy* itmp; + if (RunningInsideMus()) { + DCHECK(mus_thread_proxy_); + itmp = mus_thread_proxy_.get(); + } else { + gl_api_loader_.reset(new GlApiLoader()); + scoped_refptr<DrmThreadMessageProxy> message_proxy( + new DrmThreadMessageProxy()); + itmp = message_proxy.get(); + gpu_platform_support_.reset(new DrmGpuPlatformSupport(message_proxy)); + } + // NOTE: Can't start the thread here since this is called before sandbox - // initialization. + // initialization in multi-process Chrome. In mus, we start the DRM thread. drm_thread_.reset(new DrmThreadProxy()); + drm_thread_->BindThreadIntoMessagingProxy(itmp); surface_factory_.reset(new GbmSurfaceFactory(drm_thread_.get())); - gpu_platform_support_.reset( - new DrmGpuPlatformSupport(drm_thread_->CreateDrmThreadMessageProxy())); + if (RunningInsideMus()) { + mus_thread_proxy_->StartDrmThread(); + } } private: @@ -176,6 +210,9 @@ class OzonePlatformGbm : public OzonePlatform { scoped_ptr<DrmDisplayHostManager> display_manager_; scoped_ptr<DrmOverlayManager> overlay_manager_; + // Bridges the DRM, GPU and main threads in mus. + scoped_ptr<MusThreadProxy> mus_thread_proxy_; + #if defined(USE_XKBCOMMON) XkbEvdevCodes xkb_evdev_code_converter_; #endif diff --git a/chromium/ui/ozone/platform/egltest/BUILD.gn b/chromium/ui/ozone/platform/egltest/BUILD.gn index d6bca6cdc24..77b1566c359 100644 --- a/chromium/ui/ozone/platform/egltest/BUILD.gn +++ b/chromium/ui/ozone/platform/egltest/BUILD.gn @@ -5,6 +5,8 @@ import("//tools/generate_library_loader/generate_library_loader.gni") import("//ui/ozone/ozone.gni") +visibility = [ "//ui/ozone/*" ] + source_set("egltest") { sources = [ "client_native_pixmap_factory_egltest.cc", @@ -26,6 +28,8 @@ source_set("egltest") { "//ui/events/platform", "//ui/gfx", "//ui/ozone:ozone_base", + "//ui/ozone/common", + "//ui/platform_window", ] } @@ -69,5 +73,7 @@ if (is_linux && ozone_platform_ozonex) { deps = [ "//build/config/sanitizers:deps", ] + + visibility += [ "//chromecast/*" ] } } diff --git a/chromium/ui/ozone/platform/egltest/client_native_pixmap_factory_egltest.cc b/chromium/ui/ozone/platform/egltest/client_native_pixmap_factory_egltest.cc index 9e551f8084c..d6fe65eda7f 100644 --- a/chromium/ui/ozone/platform/egltest/client_native_pixmap_factory_egltest.cc +++ b/chromium/ui/ozone/platform/egltest/client_native_pixmap_factory_egltest.cc @@ -4,7 +4,7 @@ #include "ui/ozone/platform/egltest/client_native_pixmap_factory_egltest.h" -#include "ui/ozone/common/stub_client_native_pixmap_factory.h" // nogncheck +#include "ui/ozone/common/stub_client_native_pixmap_factory.h" namespace ui { diff --git a/chromium/ui/ozone/platform/egltest/egltest.gypi b/chromium/ui/ozone/platform/egltest/egltest.gypi index 4f79c56c4c7..8daed8cfbf9 100644 --- a/chromium/ui/ozone/platform/egltest/egltest.gypi +++ b/chromium/ui/ozone/platform/egltest/egltest.gypi @@ -19,6 +19,8 @@ 'OZONE_IMPLEMENTATION', ], 'dependencies': [ + 'ozone.gyp:ozone_base', + 'ozone.gyp:ozone_common', '../../base/base.gyp:base', '../../third_party/khronos/khronos.gyp:khronos_headers', '../events/devices/events_devices.gyp:events_devices', diff --git a/chromium/ui/ozone/platform/egltest/ozone_platform_egltest.cc b/chromium/ui/ozone/platform/egltest/ozone_platform_egltest.cc index 94012a8410a..8c300beff30 100644 --- a/chromium/ui/ozone/platform/egltest/ozone_platform_egltest.cc +++ b/chromium/ui/ozone/platform/egltest/ozone_platform_egltest.cc @@ -30,7 +30,7 @@ #include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support.h" #include "ui/ozone/public/gpu_platform_support_host.h" -#include "ui/ozone/public/ozone_platform.h" // nogncheck +#include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/ozone_switches.h" #include "ui/ozone/public/surface_factory_ozone.h" #include "ui/ozone/public/surface_ozone_egl.h" @@ -63,15 +63,10 @@ void ScaleTouchEvent(TouchEvent* event, const gfx::SizeF& size) { DeviceDataManager::GetInstance()->touchscreen_devices()) { if (device.id == event->source_device_id()) { gfx::SizeF touchscreen_size = gfx::SizeF(device.size); - gfx::PointF location = event->location_f(); - - location.Scale(size.width() / touchscreen_size.width(), - size.height() / touchscreen_size.height()); - double ratio = std::sqrt(size.GetArea() / touchscreen_size.GetArea()); - - event->set_location_f(location); - event->set_radius_x(event->pointer_details().radius_x() * ratio); - event->set_radius_y(event->pointer_details().radius_y() * ratio); + gfx::Transform transform; + transform.Scale(size.width() / touchscreen_size.width(), + size.height() / touchscreen_size.height()); + event->UpdateForRootTransform(transform); return; } } @@ -241,6 +236,18 @@ class SurfaceOzoneEgltest : public SurfaceOzoneEGL { return nullptr; } + void* /* EGLConfig */ GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) override { + EGLint broken_props[] = { + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | EGL_PBUFFER_BIT, + EGL_NONE, + }; + return ChooseEGLConfig(egl, broken_props); + } + private: LibeglplatformShimLoader* eglplatform_shim_; intptr_t native_window_; @@ -263,7 +270,6 @@ class SurfaceFactoryEgltest : public ui::SurfaceFactoryOzone { intptr_t GetNativeDisplay() override; scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget( gfx::AcceleratedWidget widget) override; - const int32_t* GetEGLSurfaceProperties(const int32_t* desired_list) override; bool LoadEGLGLES2Bindings( AddGLLibraryCallback add_gl_library, SetGLGetProcAddressProcCallback set_gl_get_proc_address) override; @@ -301,19 +307,6 @@ bool SurfaceFactoryEgltest::LoadEGLGLES2Bindings( egl_soname, gles_soname); } -const int32_t* SurfaceFactoryEgltest::GetEGLSurfaceProperties( - const int32_t* desired_list) { - DCHECK(thread_checker_.CalledOnValidThread()); - static const int32_t broken_props[] = { - EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, - EGL_SURFACE_TYPE, - EGL_WINDOW_BIT | EGL_PBUFFER_BIT, - EGL_NONE, - }; - return broken_props; -} - // Test platform for EGL. // // This is a tiny EGL-based platform. Creation of the native window is @@ -381,9 +374,6 @@ class OzonePlatformEgltest : public OzonePlatform { scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override { return make_scoped_ptr(new NativeDisplayDelegateOzone()); } - base::ScopedFD OpenClientNativePixmapDevice() const override { - return base::ScopedFD(); - } void InitializeUI() override { device_manager_ = CreateDeviceManager(); diff --git a/chromium/ui/ozone/platform/headless/BUILD.gn b/chromium/ui/ozone/platform/headless/BUILD.gn index 54ed505d1e8..4a4fdb37bed 100644 --- a/chromium/ui/ozone/platform/headless/BUILD.gn +++ b/chromium/ui/ozone/platform/headless/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +visibility = [ "//ui/ozone/*" ] + source_set("headless") { sources = [ "client_native_pixmap_factory_headless.cc", @@ -25,5 +27,7 @@ source_set("headless") { "//ui/events/platform", "//ui/gfx/geometry", "//ui/ozone:ozone_base", + "//ui/ozone/common", + "//ui/platform_window", ] } diff --git a/chromium/ui/ozone/platform/headless/client_native_pixmap_factory_headless.cc b/chromium/ui/ozone/platform/headless/client_native_pixmap_factory_headless.cc index f1e875d25e3..1f409eb013f 100644 --- a/chromium/ui/ozone/platform/headless/client_native_pixmap_factory_headless.cc +++ b/chromium/ui/ozone/platform/headless/client_native_pixmap_factory_headless.cc @@ -4,7 +4,7 @@ #include "ui/ozone/platform/headless/client_native_pixmap_factory_headless.h" -#include "ui/ozone/common/stub_client_native_pixmap_factory.h" // nogncheck +#include "ui/ozone/common/stub_client_native_pixmap_factory.h" namespace ui { diff --git a/chromium/ui/ozone/platform/headless/headless.gypi b/chromium/ui/ozone/platform/headless/headless.gypi index 65de1fa12a5..c3569929f39 100644 --- a/chromium/ui/ozone/platform/headless/headless.gypi +++ b/chromium/ui/ozone/platform/headless/headless.gypi @@ -19,6 +19,8 @@ 'OZONE_IMPLEMENTATION', ], 'dependencies': [ + 'ozone.gyp:ozone_base', + 'ozone.gyp:ozone_common', '../../base/base.gyp:base', '../base/ui_base.gyp:ui_base', '../events/events.gyp:events', diff --git a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc index c9bbe528402..a8b99525013 100644 --- a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc +++ b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc @@ -39,10 +39,10 @@ class FileSurface : public SurfaceOzoneCanvas { // SurfaceOzoneCanvas overrides: void ResizeCanvas(const gfx::Size& viewport_size) override { - surface_ = skia::AdoptRef(SkSurface::NewRaster(SkImageInfo::MakeN32Premul( - viewport_size.width(), viewport_size.height()))); + surface_ = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul( + viewport_size.width(), viewport_size.height())); } - skia::RefPtr<SkSurface> GetSurface() override { return surface_; } + sk_sp<SkSurface> GetSurface() override { return surface_; } void PresentCanvas(const gfx::Rect& damage) override { if (location_.empty()) return; @@ -62,7 +62,7 @@ class FileSurface : public SurfaceOzoneCanvas { private: base::FilePath location_; - skia::RefPtr<SkSurface> surface_; + sk_sp<SkSurface> surface_; }; class TestPixmap : public ui::NativePixmap { diff --git a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc index 509b4c4641e..49325c74ad0 100644 --- a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc +++ b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc @@ -20,7 +20,7 @@ #include "ui/ozone/public/gpu_platform_support.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" -#include "ui/ozone/public/ozone_platform.h" // nogncheck +#include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/ozone_switches.h" #include "ui/ozone/public/system_input_injector.h" @@ -78,9 +78,6 @@ class OzonePlatformHeadless : public OzonePlatform { scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override { return make_scoped_ptr(new NativeDisplayDelegateOzone()); } - base::ScopedFD OpenClientNativePixmapDevice() const override { - return base::ScopedFD(); - } void InitializeUI() override { window_manager_.reset(new HeadlessWindowManager(file_path_)); diff --git a/chromium/ui/ozone/platform/wayland/BUILD.gn b/chromium/ui/ozone/platform/wayland/BUILD.gn new file mode 100644 index 00000000000..1d8e6113da0 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/BUILD.gn @@ -0,0 +1,68 @@ +# 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. + +visibility = [ "//ui/ozone/*" ] + +source_set("wayland") { + sources = [ + "client_native_pixmap_factory_wayland.cc", + "client_native_pixmap_factory_wayland.h", + "ozone_platform_wayland.cc", + "ozone_platform_wayland.h", + "wayland_display.cc", + "wayland_display.h", + "wayland_object.cc", + "wayland_object.h", + "wayland_pointer.cc", + "wayland_pointer.h", + "wayland_surface_factory.cc", + "wayland_surface_factory.h", + "wayland_window.cc", + "wayland_window.h", + ] + + deps = [ + "//base", + "//skia", + "//third_party/wayland:wayland_client", + "//third_party/wayland-protocols:xdg_shell_protocol", + "//ui/events", + "//ui/events/platform", + "//ui/gfx", + "//ui/gfx/geometry", + "//ui/ozone:ozone_base", + "//ui/ozone/common", + "//ui/platform_window", + ] + + defines = [ "OZONE_IMPLEMENTATION" ] +} + +source_set("wayland_unittests") { + testonly = true + + sources = [ + "fake_server.cc", + "fake_server.h", + "mock_platform_window_delegate.cc", + "wayland_display_unittest.cc", + "wayland_pointer_unittest.cc", + "wayland_surface_factory_unittest.cc", + "wayland_test.cc", + "wayland_test.h", + "wayland_window_unittest.cc", + ] + + deps = [ + ":wayland", + "//testing/gmock", + "//testing/gtest", + "//third_party/wayland:wayland_server", + "//third_party/wayland-protocols:xdg_shell_protocol", + "//ui/gfx:test_support", + "//ui/ozone:platform", + ] + + defines = [ "WL_HIDE_DEPRECATED" ] +} diff --git a/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc b/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc new file mode 100644 index 00000000000..eeea61baf57 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc @@ -0,0 +1,15 @@ +// 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/wayland/client_native_pixmap_factory_wayland.h" + +#include "ui/ozone/common/stub_client_native_pixmap_factory.h" + +namespace ui { + +ClientNativePixmapFactory* CreateClientNativePixmapFactoryWayland() { + return CreateStubClientNativePixmapFactory(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.h b/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.h new file mode 100644 index 00000000000..4e0121a1e3b --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.h @@ -0,0 +1,17 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_CLIENT_NATIVE_PIXMAP_FACTORY_WAYLAND_H_ +#define UI_OZONE_PLATFORM_WAYLAND_CLIENT_NATIVE_PIXMAP_FACTORY_WAYLAND_H_ + +namespace ui { + +class ClientNativePixmapFactory; + +// Constructor hook for use in constructor_list.cc +ClientNativePixmapFactory* CreateClientNativePixmapFactoryWayland(); + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_CLIENT_NATIVE_PIXMAP_FACTORY_WAYLAND_H_ diff --git a/chromium/ui/ozone/platform/wayland/fake_server.cc b/chromium/ui/ozone/platform/wayland/fake_server.cc new file mode 100644 index 00000000000..0465728b1b2 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/fake_server.cc @@ -0,0 +1,391 @@ +// 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/wayland/fake_server.h" + +#include <sys/socket.h> +#include <wayland-server.h> +#include <xdg-shell-unstable-v5-server-protocol.h> + +#include "base/bind.h" +#include "base/files/scoped_file.h" +#include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" + +namespace wl { +namespace { + +const uint32_t kCompositorVersion = 4; +const uint32_t kSeatVersion = 4; +const uint32_t kXdgShellVersion = 1; + +void DestroyResource(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +// wl_compositor + +void CreateSurface(wl_client* client, wl_resource* resource, uint32_t id) { + auto compositor = + static_cast<MockCompositor*>(wl_resource_get_user_data(resource)); + wl_resource* surface_resource = wl_resource_create( + client, &wl_surface_interface, wl_resource_get_version(resource), id); + if (!surface_resource) { + wl_client_post_no_memory(client); + return; + } + compositor->AddSurface(make_scoped_ptr(new MockSurface(surface_resource))); +} + +const struct wl_compositor_interface compositor_impl = { + &CreateSurface, // create_surface + nullptr, // create_region +}; + +// wl_surface + +void Attach(wl_client* client, + wl_resource* resource, + wl_resource* buffer_resource, + int32_t x, + int32_t y) { + static_cast<MockSurface*>(wl_resource_get_user_data(resource)) + ->Attach(buffer_resource, x, y); +} + +void Damage(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + static_cast<MockSurface*>(wl_resource_get_user_data(resource)) + ->Damage(x, y, width, height); +} + +void Commit(wl_client* client, wl_resource* resource) { + static_cast<MockSurface*>(wl_resource_get_user_data(resource))->Commit(); +} + +const struct wl_surface_interface surface_impl = { + &DestroyResource, // destroy + &Attach, // attach + &Damage, // damage + nullptr, // frame + nullptr, // set_opaque_region + nullptr, // set_input_region + &Commit, // commit + nullptr, // set_buffer_transform + nullptr, // set_buffer_scale + nullptr, // damage_buffer +}; + +// xdg_shell + +void UseUnstableVersion(wl_client* client, + wl_resource* resource, + int32_t version) { + static_cast<MockXdgShell*>(wl_resource_get_user_data(resource)) + ->UseUnstableVersion(version); +} + +void GetXdgSurface(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* surface_resource) { + auto surface = + static_cast<MockSurface*>(wl_resource_get_user_data(surface_resource)); + if (surface->xdg_surface) { + wl_resource_post_error(resource, XDG_SHELL_ERROR_ROLE, + "surface already has a role"); + return; + } + wl_resource* xdg_surface_resource = wl_resource_create( + client, &xdg_surface_interface, wl_resource_get_version(resource), id); + if (!xdg_surface_resource) { + wl_client_post_no_memory(client); + return; + } + surface->xdg_surface.reset(new MockXdgSurface(xdg_surface_resource)); +} + +void Pong(wl_client* client, wl_resource* resource, uint32_t serial) { + static_cast<MockXdgShell*>(wl_resource_get_user_data(resource))->Pong(serial); +} + +const struct xdg_shell_interface xdg_shell_impl = { + &DestroyResource, // destroy + &UseUnstableVersion, // use_unstable_version + &GetXdgSurface, // get_xdg_surface + nullptr, // get_xdg_popup + &Pong, // pong +}; + +// wl_seat + +void GetPointer(wl_client* client, wl_resource* resource, uint32_t id) { + auto seat = static_cast<MockSeat*>(wl_resource_get_user_data(resource)); + wl_resource* pointer_resource = wl_resource_create( + client, &wl_pointer_interface, wl_resource_get_version(resource), id); + if (!pointer_resource) { + wl_client_post_no_memory(client); + return; + } + seat->pointer.reset(new MockPointer(pointer_resource)); +} + +const struct wl_seat_interface seat_impl = { + &GetPointer, // get_pointer + nullptr, // get_keyboard + nullptr, // get_touch, + &DestroyResource, // release +}; + +// wl_pointer + +const struct wl_pointer_interface pointer_impl = { + nullptr, // set_cursor + &DestroyResource, // release +}; + +// xdg_surface + +void SetTitle(wl_client* client, wl_resource* resource, const char* title) { + static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) + ->SetTitle(title); +} + +void SetAppId(wl_client* client, wl_resource* resource, const char* app_id) { + static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) + ->SetAppId(app_id); +} + +void AckConfigure(wl_client* client, wl_resource* resource, uint32_t serial) { + static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) + ->AckConfigure(serial); +} + +void SetMaximized(wl_client* client, wl_resource* resource) { + static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) + ->SetMaximized(); +} + +void UnsetMaximized(wl_client* client, wl_resource* resource) { + static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) + ->UnsetMaximized(); +} + +void SetMinimized(wl_client* client, wl_resource* resource) { + static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) + ->SetMinimized(); +} + +const struct xdg_surface_interface xdg_surface_impl = { + &DestroyResource, // destroy + nullptr, // set_parent + &SetTitle, // set_title + &SetAppId, // set_app_id + nullptr, // show_window_menu + nullptr, // move + nullptr, // resize + &AckConfigure, // ack_configure + nullptr, // set_window_geometry + &SetMaximized, // set_maximized + &UnsetMaximized, // set_unmaximized + nullptr, // set_fullscreen + nullptr, // unset_fullscreen + &SetMinimized, // set_minimized +}; + +} // namespace + +ServerObject::ServerObject(wl_resource* resource) : resource_(resource) {} + +ServerObject::~ServerObject() { + if (resource_) + wl_resource_destroy(resource_); +} + +// static +void ServerObject::OnResourceDestroyed(wl_resource* resource) { + auto obj = static_cast<ServerObject*>(wl_resource_get_user_data(resource)); + obj->resource_ = nullptr; +} + +MockXdgSurface::MockXdgSurface(wl_resource* resource) : ServerObject(resource) { + wl_resource_set_implementation(resource, &xdg_surface_impl, this, + &ServerObject::OnResourceDestroyed); +} + +MockXdgSurface::~MockXdgSurface() {} + +MockSurface::MockSurface(wl_resource* resource) : ServerObject(resource) { + wl_resource_set_implementation(resource, &surface_impl, this, + &ServerObject::OnResourceDestroyed); +} + +MockSurface::~MockSurface() { + if (xdg_surface && xdg_surface->resource()) + wl_resource_destroy(xdg_surface->resource()); +} + +MockSurface* MockSurface::FromResource(wl_resource* resource) { + if (!wl_resource_instance_of(resource, &wl_surface_interface, &surface_impl)) + return nullptr; + return static_cast<MockSurface*>(wl_resource_get_user_data(resource)); +} + +MockPointer::MockPointer(wl_resource* resource) : ServerObject(resource) { + wl_resource_set_implementation(resource, &pointer_impl, this, + &ServerObject::OnResourceDestroyed); +} + +MockPointer::~MockPointer() {} + +void GlobalDeleter::operator()(wl_global* global) { + wl_global_destroy(global); +} + +Global::Global(const wl_interface* interface, + const void* implementation, + uint32_t version) + : interface_(interface), + implementation_(implementation), + version_(version) {} + +Global::~Global() {} + +bool Global::Initialize(wl_display* display) { + global_.reset(wl_global_create(display, interface_, version_, this, &Bind)); + return global_ != nullptr; +} + +// static +void Global::Bind(wl_client* client, + void* data, + uint32_t version, + uint32_t id) { + auto global = static_cast<Global*>(data); + wl_resource* resource = wl_resource_create( + client, global->interface_, std::min(version, global->version_), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + if (!global->resource_) + global->resource_ = resource; + wl_resource_set_implementation(resource, global->implementation_, global, + &Global::OnResourceDestroyed); +} + +// static +void Global::OnResourceDestroyed(wl_resource* resource) { + auto global = static_cast<Global*>(wl_resource_get_user_data(resource)); + if (global->resource_ == resource) + global->resource_ = nullptr; +} + +MockCompositor::MockCompositor() + : Global(&wl_compositor_interface, &compositor_impl, kCompositorVersion) {} + +MockCompositor::~MockCompositor() {} + +void MockCompositor::AddSurface(scoped_ptr<MockSurface> surface) { + surfaces_.push_back(std::move(surface)); +} + +MockSeat::MockSeat() : Global(&wl_seat_interface, &seat_impl, kSeatVersion) {} + +MockSeat::~MockSeat() {} + +MockXdgShell::MockXdgShell() + : Global(&xdg_shell_interface, &xdg_shell_impl, kXdgShellVersion) {} + +MockXdgShell::~MockXdgShell() {} + +void DisplayDeleter::operator()(wl_display* display) { + wl_display_destroy(display); +} + +FakeServer::FakeServer() + : Thread("fake_wayland_server"), + pause_event_(false, false), + resume_event_(false, false) {} + +FakeServer::~FakeServer() { + Resume(); + Stop(); +} + +bool FakeServer::Start() { + display_.reset(wl_display_create()); + if (!display_) + return false; + event_loop_ = wl_display_get_event_loop(display_.get()); + + int fd[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fd) < 0) + return false; + base::ScopedFD server_fd(fd[0]); + base::ScopedFD client_fd(fd[1]); + + if (wl_display_init_shm(display_.get()) < 0) + return false; + if (!compositor_.Initialize(display_.get())) + return false; + if (!seat_.Initialize(display_.get())) + return false; + if (!xdg_shell_.Initialize(display_.get())) + return false; + + client_ = wl_client_create(display_.get(), server_fd.get()); + if (!client_) + return false; + (void)server_fd.release(); + + base::Thread::Options options; + options.message_pump_factory = + base::Bind(&FakeServer::CreateMessagePump, base::Unretained(this)); + if (!base::Thread::StartWithOptions(options)) + return false; + + setenv("WAYLAND_SOCKET", base::UintToString(client_fd.release()).c_str(), 1); + + return true; +} + +void FakeServer::Pause() { + task_runner()->PostTask( + FROM_HERE, base::Bind(&FakeServer::DoPause, base::Unretained(this))); + pause_event_.Wait(); +} + +void FakeServer::Resume() { + if (display_) + wl_display_flush_clients(display_.get()); + resume_event_.Signal(); +} + +void FakeServer::DoPause() { + base::RunLoop().RunUntilIdle(); + pause_event_.Signal(); + resume_event_.Wait(); +} + +scoped_ptr<base::MessagePump> FakeServer::CreateMessagePump() { + auto pump = make_scoped_ptr(new base::MessagePumpLibevent); + pump->WatchFileDescriptor(wl_event_loop_get_fd(event_loop_), true, + base::MessagePumpLibevent::WATCH_READ, &controller_, + this); + return std::move(pump); +} + +void FakeServer::OnFileCanReadWithoutBlocking(int fd) { + wl_event_loop_dispatch(event_loop_, 0); + wl_display_flush_clients(display_.get()); +} + +void FakeServer::OnFileCanWriteWithoutBlocking(int fd) {} + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/fake_server.h b/chromium/ui/ozone/platform/wayland/fake_server.h new file mode 100644 index 00000000000..a47c8752a92 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/fake_server.h @@ -0,0 +1,212 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_ + +#include <wayland-server-core.h> + +#include "base/bind.h" +#include "base/message_loop/message_pump_libevent.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "testing/gmock/include/gmock/gmock.h" + +struct wl_client; +struct wl_display; +struct wl_event_loop; +struct wl_global; +struct wl_resource; + +namespace wl { + +class ServerObject { + public: + ServerObject(wl_resource* resource); + virtual ~ServerObject(); + + wl_resource* resource() { return resource_; } + + static void OnResourceDestroyed(wl_resource* resource); + + private: + wl_resource* resource_; + + DISALLOW_COPY_AND_ASSIGN(ServerObject); +}; + +class MockXdgSurface : public ServerObject { + public: + MockXdgSurface(wl_resource* resource); + ~MockXdgSurface() override; + + MOCK_METHOD1(SetParent, void(wl_resource* parent)); + MOCK_METHOD1(SetTitle, void(const char* title)); + MOCK_METHOD1(SetAppId, void(const char* app_id)); + MOCK_METHOD1(AckConfigure, void(uint32_t serial)); + MOCK_METHOD0(SetMaximized, void()); + MOCK_METHOD0(UnsetMaximized, void()); + MOCK_METHOD0(SetMinimized, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockXdgSurface); +}; + +class MockSurface : public ServerObject { + public: + MockSurface(wl_resource* resource); + ~MockSurface() override; + + static MockSurface* FromResource(wl_resource* resource); + + MOCK_METHOD3(Attach, void(wl_resource* buffer, int32_t x, int32_t y)); + MOCK_METHOD4(Damage, + void(int32_t x, int32_t y, int32_t width, int32_t height)); + MOCK_METHOD0(Commit, void()); + + scoped_ptr<MockXdgSurface> xdg_surface; + + private: + DISALLOW_COPY_AND_ASSIGN(MockSurface); +}; + +class MockPointer : public ServerObject { + public: + MockPointer(wl_resource* resource); + ~MockPointer() override; + + private: + DISALLOW_COPY_AND_ASSIGN(MockPointer); +}; + +struct GlobalDeleter { + void operator()(wl_global* global); +}; + +class Global { + public: + Global(const wl_interface* interface, + const void* implementation, + uint32_t version); + virtual ~Global(); + + bool Initialize(wl_display* display); + + // The first bound resource to this global, which is usually all that is + // useful when testing a simple client. + wl_resource* resource() { return resource_; } + + static void Bind(wl_client* client, + void* data, + uint32_t version, + uint32_t id); + static void OnResourceDestroyed(wl_resource* resource); + + private: + scoped_ptr<wl_global, GlobalDeleter> global_; + + const wl_interface* interface_; + const void* implementation_; + const uint32_t version_; + wl_resource* resource_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(Global); +}; + +class MockCompositor : public Global { + public: + MockCompositor(); + ~MockCompositor() override; + + void AddSurface(scoped_ptr<MockSurface> surface); + + private: + std::vector<scoped_ptr<MockSurface>> surfaces_; + + DISALLOW_COPY_AND_ASSIGN(MockCompositor); +}; + +class MockSeat : public Global { + public: + MockSeat(); + ~MockSeat() override; + + scoped_ptr<MockPointer> pointer; + + private: + DISALLOW_COPY_AND_ASSIGN(MockSeat); +}; + +class MockXdgShell : public Global { + public: + MockXdgShell(); + ~MockXdgShell() override; + + MOCK_METHOD1(UseUnstableVersion, void(int32_t version)); + MOCK_METHOD1(Pong, void(uint32_t serial)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockXdgShell); +}; + +struct DisplayDeleter { + void operator()(wl_display* display); +}; + +class FakeServer : public base::Thread, base::MessagePumpLibevent::Watcher { + public: + FakeServer(); + ~FakeServer() override; + + // Start the fake Wayland server. If this succeeds, the WAYLAND_SOCKET + // environment variable will be set to the string representation of a file + // descriptor that a client can connect to. The caller is responsible for + // ensuring that this file descriptor gets closed (for example, by calling + // wl_display_connect). + bool Start(); + + // Pause the server when it becomes idle. + void Pause(); + + // Resume the server after flushing client connections. + void Resume(); + + template <typename T> + T* GetObject(uint32_t id) { + wl_resource* resource = wl_client_get_object(client_, id); + return resource ? T::FromResource(resource) : nullptr; + } + + MockSeat* seat() { return &seat_; } + MockXdgShell* xdg_shell() { return &xdg_shell_; } + + private: + void DoPause(); + + scoped_ptr<base::MessagePump> CreateMessagePump(); + + // base::MessagePumpLibevent::Watcher + void OnFileCanReadWithoutBlocking(int fd) override; + void OnFileCanWriteWithoutBlocking(int fd) override; + + scoped_ptr<wl_display, DisplayDeleter> display_; + wl_client* client_ = nullptr; + wl_event_loop* event_loop_ = nullptr; + + base::WaitableEvent pause_event_; + base::WaitableEvent resume_event_; + bool paused_ = false; + + MockCompositor compositor_; + MockSeat seat_; + MockXdgShell xdg_shell_; + + base::MessagePumpLibevent::FileDescriptorWatcher controller_; + + DISALLOW_COPY_AND_ASSIGN(FakeServer); +}; + +} // namespace wl + +#endif // UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_ diff --git a/chromium/ui/ozone/platform/wayland/mock_platform_window_delegate.cc b/chromium/ui/ozone/platform/wayland/mock_platform_window_delegate.cc new file mode 100644 index 00000000000..aeb57a50627 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/mock_platform_window_delegate.cc @@ -0,0 +1,13 @@ +// 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/wayland/mock_platform_window_delegate.h" + +namespace ui { + +MockPlatformWindowDelegate::MockPlatformWindowDelegate() {} + +MockPlatformWindowDelegate::~MockPlatformWindowDelegate() {} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/mock_platform_window_delegate.h b/chromium/ui/ozone/platform/wayland/mock_platform_window_delegate.h new file mode 100644 index 00000000000..d9092e5879b --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/mock_platform_window_delegate.h @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_MOCK_PLATFORM_WINDOW_DELEGATE_H_ +#define UI_OZONE_PLATFORM_WAYLAND_MOCK_PLATFORM_WINDOW_DELEGATE_H_ + +#include "ui/platform_window/platform_window_delegate.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "ui/gfx/geometry/rect.h" + +namespace ui { + +class MockPlatformWindowDelegate : public PlatformWindowDelegate { + public: + MockPlatformWindowDelegate(); + ~MockPlatformWindowDelegate(); + + MOCK_METHOD1(OnBoundsChanged, void(const gfx::Rect& new_bounds)); + MOCK_METHOD1(OnDamageRect, void(const gfx::Rect& damaged_region)); + MOCK_METHOD1(DispatchEvent, void(Event* event)); + MOCK_METHOD0(OnCloseRequest, void()); + MOCK_METHOD0(OnClosed, void()); + MOCK_METHOD1(OnWindowStateChanged, void(PlatformWindowState new_state)); + MOCK_METHOD0(OnLostCapture, void()); + MOCK_METHOD2(OnAcceleratedWidgetAvailable, + void(gfx::AcceleratedWidget widget, float device_pixel_ratio)); + MOCK_METHOD0(OnAcceleratedWidgetDestroyed, void()); + MOCK_METHOD1(OnActivationChanged, void(bool active)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockPlatformWindowDelegate); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_MOCK_PLATFORM_WINDOW_DELEGATE_H_ diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc new file mode 100644 index 00000000000..9e5eb150ce3 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -0,0 +1,109 @@ +// 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/wayland/ozone_platform_wayland.h" + +#include "ui/ozone/common/native_display_delegate_ozone.h" +#include "ui/ozone/common/stub_overlay_manager.h" +#include "ui/ozone/platform/wayland/wayland_display.h" +#include "ui/ozone/platform/wayland/wayland_surface_factory.h" +#include "ui/ozone/platform/wayland/wayland_window.h" +#include "ui/ozone/public/cursor_factory_ozone.h" +#include "ui/ozone/public/gpu_platform_support.h" +#include "ui/ozone/public/gpu_platform_support_host.h" +#include "ui/ozone/public/input_controller.h" +#include "ui/ozone/public/ozone_platform.h" +#include "ui/ozone/public/system_input_injector.h" + +namespace ui { + +namespace { + +class OzonePlatformWayland : public OzonePlatform { + public: + OzonePlatformWayland() {} + ~OzonePlatformWayland() override {} + + // OzonePlatform + SurfaceFactoryOzone* GetSurfaceFactoryOzone() override { + return surface_factory_.get(); + } + + OverlayManagerOzone* GetOverlayManager() override { + return overlay_manager_.get(); + } + + CursorFactoryOzone* GetCursorFactoryOzone() override { + return cursor_factory_.get(); + } + + InputController* GetInputController() override { + return input_controller_.get(); + } + + GpuPlatformSupport* GetGpuPlatformSupport() override { + return gpu_platform_support_.get(); + } + + GpuPlatformSupportHost* GetGpuPlatformSupportHost() override { + return gpu_platform_support_host_.get(); + } + + scoped_ptr<SystemInputInjector> CreateSystemInputInjector() override { + return nullptr; + } + + scoped_ptr<PlatformWindow> CreatePlatformWindow( + PlatformWindowDelegate* delegate, + const gfx::Rect& bounds) override { + auto window = + make_scoped_ptr(new WaylandWindow(delegate, display_.get(), bounds)); + if (!window->Initialize()) + return nullptr; + return std::move(window); + } + + scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override { + return make_scoped_ptr(new NativeDisplayDelegateOzone); + } + + void InitializeUI() override { + display_.reset(new WaylandDisplay); + if (!display_->Initialize()) + LOG(FATAL) << "Failed to initialize Wayland platform"; + + cursor_factory_.reset(new CursorFactoryOzone); + overlay_manager_.reset(new StubOverlayManager); + input_controller_ = CreateStubInputController(); + surface_factory_.reset(new WaylandSurfaceFactory(display_.get())); + gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); + } + + void InitializeGPU() override { + // Don't reinitialize the surface factory in case InitializeUI was called + // previously in the same process. + if (!surface_factory_) + surface_factory_.reset(new WaylandSurfaceFactory(nullptr)); + gpu_platform_support_.reset(CreateStubGpuPlatformSupport()); + } + + private: + scoped_ptr<WaylandDisplay> display_; + scoped_ptr<WaylandSurfaceFactory> surface_factory_; + scoped_ptr<CursorFactoryOzone> cursor_factory_; + scoped_ptr<StubOverlayManager> overlay_manager_; + scoped_ptr<InputController> input_controller_; + scoped_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; + scoped_ptr<GpuPlatformSupport> gpu_platform_support_; + + DISALLOW_COPY_AND_ASSIGN(OzonePlatformWayland); +}; + +} // namespace + +OzonePlatform* CreateOzonePlatformWayland() { + return new OzonePlatformWayland; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.h b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.h new file mode 100644 index 00000000000..2eb802cd466 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.h @@ -0,0 +1,17 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_WAYLAND_H_ +#define UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_WAYLAND_H_ + +namespace ui { + +class OzonePlatform; + +// Constructor hook for use in ozone_platform_list.cc +OzonePlatform* CreateOzonePlatformWayland(); + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_DRM_OZONE_PLATFORM_WAYLAND_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland.gypi b/chromium/ui/ozone/platform/wayland/wayland.gypi new file mode 100644 index 00000000000..c71b6346172 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland.gypi @@ -0,0 +1,87 @@ +# 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. + +{ + 'variables': { + 'internal_ozone_platform_deps': [ + 'ozone_platform_wayland', + ], + 'internal_ozone_platform_unittest_deps': [ + 'ozone_platform_wayland_unittests', + ], + 'internal_ozone_platforms': [ + 'wayland' + ], + }, + 'targets': [ + { + 'target_name': 'ozone_platform_wayland', + 'type': 'static_library', + 'defines': [ + 'OZONE_IMPLEMENTATION', + ], + 'dependencies': [ + 'ozone.gyp:ozone_base', + 'ozone.gyp:ozone_common', + '../../base/base.gyp:base', + '../../skia/skia.gyp:skia', + '../../third_party/wayland-protocols/wayland-protocols.gyp:xdg_shell_protocol', + '../../third_party/wayland/wayland.gyp:wayland_client', + '../events/events.gyp:events', + '../events/platform/events_platform.gyp:events_platform', + ], + 'sources': [ + 'client_native_pixmap_factory_wayland.cc', + 'client_native_pixmap_factory_wayland.h', + 'ozone_platform_wayland.cc', + 'ozone_platform_wayland.h', + 'wayland_display.cc', + 'wayland_display.h', + 'wayland_object.cc', + 'wayland_object.h', + 'wayland_pointer.cc', + 'wayland_pointer.h', + 'wayland_surface_factory.cc', + 'wayland_surface_factory.h', + 'wayland_window.cc', + 'wayland_window.h', + ], + }, + { + 'target_name': 'ozone_platform_wayland_unittests', + 'type': 'none', + 'dependencies': [ + 'ozone.gyp:ozone_platform', + '../../skia/skia.gyp:skia', + '../../testing/gmock.gyp:gmock', + '../../third_party/wayland-protocols/wayland-protocols.gyp:xdg_shell_protocol', + '../../third_party/wayland/wayland.gyp:wayland_server', + '../gfx/gfx.gyp:gfx_test_support', + ], + 'export_dependent_settings': [ + '../../skia/skia.gyp:skia', + '../../testing/gmock.gyp:gmock', + '../../third_party/wayland-protocols/wayland-protocols.gyp:xdg_shell_protocol', + '../../third_party/wayland/wayland.gyp:wayland_server', + '../gfx/gfx.gyp:gfx_test_support', + ], + 'direct_dependent_settings': { + 'defines': [ + 'WL_HIDE_DEPRECATED', + ], + 'sources': [ + 'fake_server.cc', + 'fake_server.h', + 'mock_platform_window_delegate.cc', + 'wayland_display_unittest.cc', + 'wayland_pointer_unittest.cc', + 'wayland_surface_factory_unittest.cc', + 'wayland_test.cc', + 'wayland_test.h', + 'wayland_window_unittest.cc', + ], + }, + }, + ], +} diff --git a/chromium/ui/ozone/platform/wayland/wayland_display.cc b/chromium/ui/ozone/platform/wayland/wayland_display.cc new file mode 100644 index 00000000000..a34abe266a5 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_display.cc @@ -0,0 +1,215 @@ +// 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/wayland/wayland_display.h" + +#include <xdg-shell-unstable-v5-client-protocol.h> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "ui/ozone/platform/wayland/wayland_object.h" +#include "ui/ozone/platform/wayland/wayland_window.h" + +static_assert(XDG_SHELL_VERSION_CURRENT == 5, "Unsupported xdg-shell version"); + +namespace ui { +namespace { +const uint32_t kMaxCompositorVersion = 4; +const uint32_t kMaxSeatVersion = 4; +const uint32_t kMaxShmVersion = 1; +const uint32_t kMaxXdgShellVersion = 1; +} // namespace + +WaylandDisplay::WaylandDisplay() {} + +WaylandDisplay::~WaylandDisplay() {} + +bool WaylandDisplay::Initialize() { + static const wl_registry_listener registry_listener = { + &WaylandDisplay::Global, &WaylandDisplay::GlobalRemove, + }; + + display_.reset(wl_display_connect(nullptr)); + if (!display_) { + LOG(ERROR) << "Failed to connect to Wayland display"; + return false; + } + + registry_.reset(wl_display_get_registry(display_.get())); + if (!registry_) { + LOG(ERROR) << "Failed to get Wayland registry"; + return false; + } + + wl_registry_add_listener(registry_.get(), ®istry_listener, this); + wl_display_roundtrip(display_.get()); + + if (!compositor_) { + LOG(ERROR) << "No wl_compositor object"; + return false; + } + if (!shm_) { + LOG(ERROR) << "No wl_shm object"; + return false; + } + if (!seat_) { + LOG(ERROR) << "No wl_seat object"; + return false; + } + if (!shell_) { + LOG(ERROR) << "No xdg_shell object"; + return false; + } + + return true; +} + +bool WaylandDisplay::StartProcessingEvents() { + if (watching_) + return true; + + DCHECK(display_); + wl_display_flush(display_.get()); + + DCHECK(base::MessageLoopForUI::IsCurrent()); + if (!base::MessageLoopForUI::current()->WatchFileDescriptor( + wl_display_get_fd(display_.get()), true, + base::MessagePumpLibevent::WATCH_READ, &controller_, this)) + return false; + + watching_ = true; + return true; +} + +void WaylandDisplay::ScheduleFlush() { + if (scheduled_flush_ || !watching_) + return; + base::MessageLoopForUI::current()->task_runner()->PostTask( + FROM_HERE, base::Bind(&WaylandDisplay::Flush, base::Unretained(this))); + scheduled_flush_ = true; +} + +WaylandWindow* WaylandDisplay::GetWindow(gfx::AcceleratedWidget widget) { + auto it = window_map_.find(widget); + return it == window_map_.end() ? nullptr : it->second; +} + +void WaylandDisplay::AddWindow(gfx::AcceleratedWidget widget, + WaylandWindow* window) { + window_map_[widget] = window; +} + +void WaylandDisplay::RemoveWindow(gfx::AcceleratedWidget widget) { + window_map_.erase(widget); +} + +void WaylandDisplay::OnDispatcherListChanged() { + StartProcessingEvents(); +} + +void WaylandDisplay::Flush() { + wl_display_flush(display_.get()); + scheduled_flush_ = false; +} + +void WaylandDisplay::DispatchUiEvent(Event* event) { + PlatformEventSource::DispatchEvent(event); +} + +void WaylandDisplay::OnFileCanReadWithoutBlocking(int fd) { + wl_display_dispatch(display_.get()); + for (const auto& window : window_map_) + window.second->ApplyPendingBounds(); +} + +void WaylandDisplay::OnFileCanWriteWithoutBlocking(int fd) {} + +// static +void WaylandDisplay::Global(void* data, + wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) { + static const wl_seat_listener seat_listener = { + &WaylandDisplay::Capabilities, &WaylandDisplay::Name, + }; + static const xdg_shell_listener shell_listener = { + &WaylandDisplay::Ping, + }; + + WaylandDisplay* display = static_cast<WaylandDisplay*>(data); + if (!display->compositor_ && strcmp(interface, "wl_compositor") == 0) { + display->compositor_ = wl::Bind<wl_compositor>( + registry, name, std::min(version, kMaxCompositorVersion)); + if (!display->compositor_) + LOG(ERROR) << "Failed to bind to wl_compositor global"; + } else if (!display->shm_ && strcmp(interface, "wl_shm") == 0) { + display->shm_ = + wl::Bind<wl_shm>(registry, name, std::min(version, kMaxShmVersion)); + if (!display->shm_) + LOG(ERROR) << "Failed to bind to wl_shm global"; + } else if (!display->seat_ && strcmp(interface, "wl_seat") == 0) { + display->seat_ = + wl::Bind<wl_seat>(registry, name, std::min(version, kMaxSeatVersion)); + if (!display->seat_) { + LOG(ERROR) << "Failed to bind to wl_seat global"; + return; + } + wl_seat_add_listener(display->seat_.get(), &seat_listener, display); + } else if (!display->shell_ && strcmp(interface, "xdg_shell") == 0) { + display->shell_ = wl::Bind<xdg_shell>( + registry, name, std::min(version, kMaxXdgShellVersion)); + if (!display->shell_) { + LOG(ERROR) << "Failed to bind to xdg_shell global"; + return; + } + xdg_shell_add_listener(display->shell_.get(), &shell_listener, display); + xdg_shell_use_unstable_version(display->shell_.get(), + XDG_SHELL_VERSION_CURRENT); + } + + display->ScheduleFlush(); +} + +// static +void WaylandDisplay::GlobalRemove(void* data, + wl_registry* registry, + uint32_t name) { + NOTIMPLEMENTED(); +} + +// static +void WaylandDisplay::Capabilities(void* data, + wl_seat* seat, + uint32_t capabilities) { + WaylandDisplay* display = static_cast<WaylandDisplay*>(data); + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + if (!display->pointer_) { + wl_pointer* pointer = wl_seat_get_pointer(display->seat_.get()); + if (!pointer) { + LOG(ERROR) << "Failed to get wl_pointer from seat"; + return; + } + display->pointer_ = make_scoped_ptr(new WaylandPointer( + pointer, base::Bind(&WaylandDisplay::DispatchUiEvent, + base::Unretained(display)))); + } + } else if (display->pointer_) { + display->pointer_.reset(); + } + display->ScheduleFlush(); +} + +// static +void WaylandDisplay::Name(void* data, wl_seat* seat, const char* name) {} + +// static +void WaylandDisplay::Ping(void* data, xdg_shell* shell, uint32_t serial) { + WaylandDisplay* display = static_cast<WaylandDisplay*>(data); + xdg_shell_pong(shell, serial); + display->ScheduleFlush(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_display.h b/chromium/ui/ozone/platform/wayland/wayland_display.h new file mode 100644 index 00000000000..eb72c9ccf07 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_display.h @@ -0,0 +1,87 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_DISPLAY_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_DISPLAY_H_ + +#include <map> + +#include "base/message_loop/message_pump_libevent.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/wayland_object.h" +#include "ui/ozone/platform/wayland/wayland_pointer.h" + +namespace ui { + +class WaylandWindow; + +class WaylandDisplay : public PlatformEventSource, + public base::MessagePumpLibevent::Watcher { + public: + WaylandDisplay(); + ~WaylandDisplay() override; + + bool Initialize(); + bool StartProcessingEvents(); + + // Schedules a flush of the Wayland connection. + void ScheduleFlush(); + + wl_display* display() { return display_.get(); } + wl_compositor* compositor() { return compositor_.get(); } + wl_shm* shm() { return shm_.get(); } + xdg_shell* shell() { return shell_.get(); } + + WaylandWindow* GetWindow(gfx::AcceleratedWidget widget); + void AddWindow(gfx::AcceleratedWidget widget, WaylandWindow* window); + void RemoveWindow(gfx::AcceleratedWidget widget); + + private: + void Flush(); + void DispatchUiEvent(Event* event); + + // PlatformEventSource + void OnDispatcherListChanged() override; + + // base::MessagePumpLibevent::Watcher + void OnFileCanReadWithoutBlocking(int fd) override; + void OnFileCanWriteWithoutBlocking(int fd) override; + + // wl_registry_listener + static void Global(void* data, + wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version); + static void GlobalRemove(void* data, wl_registry* registry, uint32_t name); + + // wl_seat_listener + static void Capabilities(void* data, wl_seat* seat, uint32_t capabilities); + static void Name(void* data, wl_seat* seat, const char* name); + + // xdg_shell_listener + static void Ping(void* data, xdg_shell* shell, uint32_t serial); + + std::map<gfx::AcceleratedWidget, WaylandWindow*> window_map_; + + wl::Object<wl_display> display_; + wl::Object<wl_registry> registry_; + wl::Object<wl_compositor> compositor_; + wl::Object<wl_seat> seat_; + wl::Object<wl_shm> shm_; + wl::Object<xdg_shell> shell_; + + scoped_ptr<WaylandPointer> pointer_; + + bool scheduled_flush_ = false; + bool watching_ = false; + base::MessagePumpLibevent::FileDescriptorWatcher controller_; + + DISALLOW_COPY_AND_ASSIGN(WaylandDisplay); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_DISPLAY_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_display_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_display_unittest.cc new file mode 100644 index 00000000000..3b37154a689 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_display_unittest.cc @@ -0,0 +1,48 @@ +// 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 <wayland-server-core.h> +#include <xdg-shell-unstable-v5-server-protocol.h> + +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/ozone/platform/wayland/fake_server.h" +#include "ui/ozone/platform/wayland/wayland_display.h" + +namespace ui { + +TEST(WaylandDisplayTest, UseUnstableVersion) { + base::MessageLoopForUI message_loop; + wl::FakeServer server; + EXPECT_CALL(*server.xdg_shell(), + UseUnstableVersion(XDG_SHELL_VERSION_CURRENT)); + ASSERT_TRUE(server.Start()); + WaylandDisplay display; + ASSERT_TRUE(display.Initialize()); + display.StartProcessingEvents(); + + base::RunLoop().RunUntilIdle(); + server.Pause(); +} + +TEST(WaylandDisplayTest, Ping) { + base::MessageLoopForUI message_loop; + wl::FakeServer server; + ASSERT_TRUE(server.Start()); + WaylandDisplay display; + ASSERT_TRUE(display.Initialize()); + display.StartProcessingEvents(); + + base::RunLoop().RunUntilIdle(); + server.Pause(); + + xdg_shell_send_ping(server.xdg_shell()->resource(), 1234); + EXPECT_CALL(*server.xdg_shell(), Pong(1234)); + + server.Resume(); + base::RunLoop().RunUntilIdle(); + server.Pause(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_object.cc b/chromium/ui/ozone/platform/wayland/wayland_object.cc new file mode 100644 index 00000000000..6b7f1e185ae --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_object.cc @@ -0,0 +1,67 @@ +// 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/wayland/wayland_object.h" + +#include <wayland-client.h> +#include <xdg-shell-unstable-v5-client-protocol.h> + +namespace wl { +namespace { + +void delete_pointer(wl_pointer* pointer) { + if (wl_pointer_get_version(pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) + wl_pointer_release(pointer); + else + wl_pointer_destroy(pointer); +} + +void delete_seat(wl_seat* seat) { + if (wl_seat_get_version(seat) >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_seat_release(seat); + else + wl_seat_destroy(seat); +} + +} // namespace + +const wl_interface* ObjectTraits<wl_buffer>::interface = &wl_buffer_interface; +void (*ObjectTraits<wl_buffer>::deleter)(wl_buffer*) = &wl_buffer_destroy; + +const wl_interface* ObjectTraits<wl_compositor>::interface = + &wl_compositor_interface; +void (*ObjectTraits<wl_compositor>::deleter)(wl_compositor*) = + &wl_compositor_destroy; + +const wl_interface* ObjectTraits<wl_display>::interface = &wl_display_interface; +void (*ObjectTraits<wl_display>::deleter)(wl_display*) = &wl_display_disconnect; + +const wl_interface* ObjectTraits<wl_pointer>::interface = &wl_pointer_interface; +void (*ObjectTraits<wl_pointer>::deleter)(wl_pointer*) = &delete_pointer; + +const wl_interface* ObjectTraits<wl_registry>::interface = + &wl_registry_interface; +void (*ObjectTraits<wl_registry>::deleter)(wl_registry*) = &wl_registry_destroy; + +const wl_interface* ObjectTraits<wl_seat>::interface = &wl_seat_interface; +void (*ObjectTraits<wl_seat>::deleter)(wl_seat*) = &delete_seat; + +const wl_interface* ObjectTraits<wl_shm>::interface = &wl_shm_interface; +void (*ObjectTraits<wl_shm>::deleter)(wl_shm*) = &wl_shm_destroy; + +const wl_interface* ObjectTraits<wl_shm_pool>::interface = + &wl_shm_pool_interface; +void (*ObjectTraits<wl_shm_pool>::deleter)(wl_shm_pool*) = &wl_shm_pool_destroy; + +const wl_interface* ObjectTraits<wl_surface>::interface = &wl_surface_interface; +void (*ObjectTraits<wl_surface>::deleter)(wl_surface*) = &wl_surface_destroy; + +const wl_interface* ObjectTraits<xdg_shell>::interface = &xdg_shell_interface; +void (*ObjectTraits<xdg_shell>::deleter)(xdg_shell*) = &xdg_shell_destroy; + +const wl_interface* ObjectTraits<xdg_surface>::interface = + &xdg_surface_interface; +void (*ObjectTraits<xdg_surface>::deleter)(xdg_surface*) = &xdg_surface_destroy; + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/wayland_object.h b/chromium/ui/ozone/platform/wayland/wayland_object.h new file mode 100644 index 00000000000..f5324f9bf15 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_object.h @@ -0,0 +1,121 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OBJECT_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OBJECT_H_ + +#include <wayland-client-core.h> + +#include "base/memory/scoped_ptr.h" + +struct wl_buffer; +struct wl_compositor; +struct wl_pointer; +struct wl_registry; +struct wl_seat; +struct wl_shm; +struct wl_shm_pool; +struct wl_surface; +struct xdg_shell; +struct xdg_surface; + +namespace wl { + +template <typename T> +struct ObjectTraits; + +template <> +struct ObjectTraits<wl_buffer> { + static const wl_interface* interface; + static void (*deleter)(wl_buffer*); +}; + +template <> +struct ObjectTraits<wl_compositor> { + static const wl_interface* interface; + static void (*deleter)(wl_compositor*); +}; + +template <> +struct ObjectTraits<wl_display> { + static const wl_interface* interface; + static void (*deleter)(wl_display*); +}; + +template <> +struct ObjectTraits<wl_pointer> { + static const wl_interface* interface; + static void (*deleter)(wl_pointer*); +}; + +template <> +struct ObjectTraits<wl_registry> { + static const wl_interface* interface; + static void (*deleter)(wl_registry*); +}; + +template <> +struct ObjectTraits<wl_seat> { + static const wl_interface* interface; + static void (*deleter)(wl_seat*); +}; + +template <> +struct ObjectTraits<wl_shm> { + static const wl_interface* interface; + static void (*deleter)(wl_shm*); +}; + +template <> +struct ObjectTraits<wl_shm_pool> { + static const wl_interface* interface; + static void (*deleter)(wl_shm_pool*); +}; + +template <> +struct ObjectTraits<wl_surface> { + static const wl_interface* interface; + static void (*deleter)(wl_surface*); +}; + +template <> +struct ObjectTraits<xdg_shell> { + static const wl_interface* interface; + static void (*deleter)(xdg_shell*); +}; + +template <> +struct ObjectTraits<xdg_surface> { + static const wl_interface* interface; + static void (*deleter)(xdg_surface*); +}; + +struct Deleter { + template <typename T> + void operator()(T* obj) { + ObjectTraits<T>::deleter(obj); + } +}; + +template <typename T> +class Object : public scoped_ptr<T, Deleter> { + public: + Object() {} + explicit Object(T* obj) : scoped_ptr<T, Deleter>(obj) {} + + uint32_t id() { + return wl_proxy_get_id( + reinterpret_cast<wl_proxy*>(scoped_ptr<T, Deleter>::get())); + } +}; + +template <typename T> +wl::Object<T> Bind(wl_registry* registry, uint32_t name, uint32_t version) { + return wl::Object<T>(static_cast<T*>( + wl_registry_bind(registry, name, ObjectTraits<T>::interface, version))); +} + +} // namespace wl + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OBJECT_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/wayland_pointer.cc new file mode 100644 index 00000000000..7cdb4fbb511 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer.cc @@ -0,0 +1,143 @@ +// 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/wayland/wayland_pointer.h" + +#include <linux/input.h> +#include <wayland-client.h> + +#include "ui/events/event.h" +#include "ui/ozone/platform/wayland/wayland_window.h" + +// TODO(forney): Handle version 5 of wl_pointer. + +namespace ui { + +WaylandPointer::WaylandPointer(wl_pointer* pointer, + const EventDispatchCallback& callback) + : obj_(pointer), callback_(callback) { + static const wl_pointer_listener listener = { + &WaylandPointer::Enter, &WaylandPointer::Leave, &WaylandPointer::Motion, + &WaylandPointer::Button, &WaylandPointer::Axis, + }; + + wl_pointer_add_listener(obj_.get(), &listener, this); +} + +WaylandPointer::~WaylandPointer() {} + +// static +void WaylandPointer::Enter(void* data, + wl_pointer* obj, + uint32_t serial, + wl_surface* surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y) { + WaylandPointer* pointer = static_cast<WaylandPointer*>(data); + pointer->location_.SetPoint(wl_fixed_to_double(surface_x), + wl_fixed_to_double(surface_y)); + if (surface) + WaylandWindow::FromSurface(surface)->set_pointer_focus(true); +} + +// static +void WaylandPointer::Leave(void* data, + wl_pointer* obj, + uint32_t serial, + wl_surface* surface) { + if (surface) + WaylandWindow::FromSurface(surface)->set_pointer_focus(false); +} + +// static +void WaylandPointer::Motion(void* data, + wl_pointer* obj, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y) { + WaylandPointer* pointer = static_cast<WaylandPointer*>(data); + pointer->location_.SetPoint(wl_fixed_to_double(surface_x), + wl_fixed_to_double(surface_y)); + MouseEvent event(ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), + base::TimeDelta::FromMilliseconds(time), pointer->flags_, 0); + event.set_location_f(pointer->location_); + event.set_root_location_f(pointer->location_); + pointer->callback_.Run(&event); +} + +// static +void WaylandPointer::Button(void* data, + wl_pointer* obj, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) { + WaylandPointer* pointer = static_cast<WaylandPointer*>(data); + int flag; + switch (button) { + case BTN_LEFT: + flag = EF_LEFT_MOUSE_BUTTON; + break; + case BTN_MIDDLE: + flag = EF_MIDDLE_MOUSE_BUTTON; + break; + case BTN_RIGHT: + flag = EF_RIGHT_MOUSE_BUTTON; + break; + case BTN_BACK: + flag = EF_BACK_MOUSE_BUTTON; + break; + case BTN_FORWARD: + flag = EF_FORWARD_MOUSE_BUTTON; + break; + default: + return; + } + int flags = pointer->flags_ | flag; + EventType type; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + type = ET_MOUSE_PRESSED; + pointer->flags_ |= flag; + } else { + type = ET_MOUSE_RELEASED; + pointer->flags_ &= ~flag; + } + MouseEvent event(type, gfx::Point(), gfx::Point(), + base::TimeDelta::FromMilliseconds(time), flags, flag); + event.set_location_f(pointer->location_); + event.set_root_location_f(pointer->location_); + pointer->callback_.Run(&event); +} + +// static +void WaylandPointer::Axis(void* data, + wl_pointer* obj, + uint32_t time, + uint32_t axis, + wl_fixed_t value) { + static const double kAxisValueScale = 10.0; + WaylandPointer* pointer = static_cast<WaylandPointer*>(data); + gfx::Vector2d offset; + // Wayland compositors send axis events with values in the surface coordinate + // space. They send a value of 10 per mouse wheel click by convention, so + // clients (e.g. GTK+) typically scale down by this amount to convert to + // discrete step coordinates. wl_pointer version 5 improves the situation by + // adding axis sources and discrete axis events. + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + offset.set_y(-wl_fixed_to_double(value) / kAxisValueScale * + MouseWheelEvent::kWheelDelta); + else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + offset.set_x(wl_fixed_to_double(value) / kAxisValueScale * + MouseWheelEvent::kWheelDelta); + else + return; + MouseWheelEvent event(offset, gfx::Point(), gfx::Point(), + base::TimeDelta::FromMilliseconds(time), + pointer->flags_, 0); + event.set_location_f(pointer->location_); + event.set_root_location_f(pointer->location_); + pointer->callback_.Run(&event); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/wayland_pointer.h new file mode 100644 index 00000000000..bee75a9e4bb --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer.h @@ -0,0 +1,56 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_POINTER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_POINTER_H_ + +#include "ui/events/ozone/evdev/event_dispatch_callback.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/ozone/platform/wayland/wayland_object.h" + +namespace ui { + +class WaylandPointer { + public: + WaylandPointer(wl_pointer* pointer, const EventDispatchCallback& callback); + virtual ~WaylandPointer(); + + private: + // wl_pointer_listener + static void Enter(void* data, + wl_pointer* obj, + uint32_t serial, + wl_surface* surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y); + static void Leave(void* data, + wl_pointer* obj, + uint32_t serial, + wl_surface* surface); + static void Motion(void* data, + wl_pointer* obj, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y); + static void Button(void* data, + wl_pointer* obj, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state); + static void Axis(void* data, + wl_pointer* obj, + uint32_t time, + uint32_t axis, + wl_fixed_t value); + + wl::Object<wl_pointer> obj_; + EventDispatchCallback callback_; + gfx::PointF location_; + int flags_ = 0; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_POINTER_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_pointer_unittest.cc new file mode 100644 index 00000000000..71e28bed3c9 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer_unittest.cc @@ -0,0 +1,232 @@ +// 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 <linux/input.h> +#include <wayland-server.h> + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" +#include "ui/ozone/platform/wayland/fake_server.h" +#include "ui/ozone/platform/wayland/mock_platform_window_delegate.h" +#include "ui/ozone/platform/wayland/wayland_test.h" +#include "ui/ozone/platform/wayland/wayland_window.h" + +using ::testing::SaveArg; +using ::testing::_; + +namespace ui { + +class WaylandPointerTest : public WaylandTest { + public: + WaylandPointerTest() {} + + void SetUp() override { + WaylandTest::SetUp(); + + wl_seat_send_capabilities(server.seat()->resource(), + WL_SEAT_CAPABILITY_POINTER); + + Sync(); + + pointer = server.seat()->pointer.get(); + ASSERT_TRUE(pointer); + } + + protected: + wl::MockPointer* pointer; + + private: + DISALLOW_COPY_AND_ASSIGN(WaylandPointerTest); +}; + +TEST_F(WaylandPointerTest, Leave) { + MockPlatformWindowDelegate other_delegate; + WaylandWindow other_window(&other_delegate, &display, + gfx::Rect(0, 0, 10, 10)); + gfx::AcceleratedWidget other_widget = gfx::kNullAcceleratedWidget; + EXPECT_CALL(other_delegate, OnAcceleratedWidgetAvailable(_, _)) + .WillOnce(SaveArg<0>(&other_widget)); + ASSERT_TRUE(other_window.Initialize()); + ASSERT_NE(other_widget, gfx::kNullAcceleratedWidget); + + Sync(); + + wl::MockSurface* other_surface = + server.GetObject<wl::MockSurface>(other_widget); + ASSERT_TRUE(other_surface); + + wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0); + wl_pointer_send_leave(pointer->resource(), 2, surface->resource()); + wl_pointer_send_enter(pointer->resource(), 3, other_surface->resource(), 0, + 0); + wl_pointer_send_button(pointer->resource(), 4, 1004, BTN_LEFT, + WL_POINTER_BUTTON_STATE_PRESSED); + EXPECT_CALL(delegate, DispatchEvent(_)).Times(0); + + // Do an extra Sync() here so that we process the second enter event before we + // destroy |other_window|. + Sync(); +} + +ACTION_P(CloneEvent, ptr) { + *ptr = Event::Clone(*arg0); +} + +TEST_F(WaylandPointerTest, Motion) { + wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0); + wl_pointer_send_motion(pointer->resource(), 1002, wl_fixed_from_double(10.75), + wl_fixed_from_double(20.375)); + + scoped_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + + Sync(); + + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + auto mouse_event = static_cast<MouseEvent*>(event.get()); + EXPECT_EQ(ET_MOUSE_MOVED, mouse_event->type()); + EXPECT_EQ(0, mouse_event->button_flags()); + EXPECT_EQ(0, mouse_event->changed_button_flags()); + // TODO(forney): Once crbug.com/337827 is solved, compare with the fractional + // coordinates sent above. + EXPECT_EQ(gfx::PointF(10, 20), mouse_event->location_f()); + EXPECT_EQ(gfx::PointF(10, 20), mouse_event->root_location_f()); +} + +TEST_F(WaylandPointerTest, MotionDragged) { + wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0); + wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_MIDDLE, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + scoped_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + wl_pointer_send_motion(pointer->resource(), 1003, wl_fixed_from_int(400), + wl_fixed_from_int(500)); + + Sync(); + + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + auto mouse_event = static_cast<MouseEvent*>(event.get()); + EXPECT_EQ(ET_MOUSE_DRAGGED, mouse_event->type()); + EXPECT_EQ(EF_MIDDLE_MOUSE_BUTTON, mouse_event->button_flags()); + EXPECT_EQ(0, mouse_event->changed_button_flags()); + EXPECT_EQ(gfx::PointF(400, 500), mouse_event->location_f()); + EXPECT_EQ(gfx::PointF(400, 500), mouse_event->root_location_f()); +} + +TEST_F(WaylandPointerTest, ButtonPress) { + wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_fixed_from_int(200), wl_fixed_from_int(150)); + wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_RIGHT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + scoped_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + wl_pointer_send_button(pointer->resource(), 3, 1003, BTN_LEFT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + auto mouse_event = static_cast<MouseEvent*>(event.get()); + EXPECT_EQ(ET_MOUSE_PRESSED, mouse_event->type()); + EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, + mouse_event->button_flags()); + EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, mouse_event->changed_button_flags()); + EXPECT_EQ(gfx::PointF(200, 150), mouse_event->location_f()); + EXPECT_EQ(gfx::PointF(200, 150), mouse_event->root_location_f()); +} + +TEST_F(WaylandPointerTest, ButtonRelease) { + wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_fixed_from_int(50), wl_fixed_from_int(50)); + wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_BACK, + WL_POINTER_BUTTON_STATE_PRESSED); + wl_pointer_send_button(pointer->resource(), 3, 1003, BTN_LEFT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + scoped_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + wl_pointer_send_button(pointer->resource(), 4, 1004, BTN_LEFT, + WL_POINTER_BUTTON_STATE_RELEASED); + + Sync(); + + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + auto mouse_event = static_cast<MouseEvent*>(event.get()); + EXPECT_EQ(ET_MOUSE_RELEASED, mouse_event->type()); + EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_BACK_MOUSE_BUTTON, + mouse_event->button_flags()); + EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, mouse_event->changed_button_flags()); + EXPECT_EQ(gfx::PointF(50, 50), mouse_event->location_f()); + EXPECT_EQ(gfx::PointF(50, 50), mouse_event->root_location_f()); +} + +TEST_F(WaylandPointerTest, AxisVertical) { + wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_fixed_from_int(0), wl_fixed_from_int(0)); + wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_RIGHT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + scoped_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + // Wayland servers typically send a value of 10 per mouse wheel click. + wl_pointer_send_axis(pointer->resource(), 1003, + WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_from_int(20)); + + Sync(); + + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseWheelEvent()); + auto mouse_wheel_event = static_cast<MouseWheelEvent*>(event.get()); + EXPECT_EQ(gfx::Vector2d(0, -2 * MouseWheelEvent::kWheelDelta), + mouse_wheel_event->offset()); + EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, mouse_wheel_event->button_flags()); + EXPECT_EQ(0, mouse_wheel_event->changed_button_flags()); + EXPECT_EQ(gfx::PointF(), mouse_wheel_event->location_f()); + EXPECT_EQ(gfx::PointF(), mouse_wheel_event->root_location_f()); +} + +TEST_F(WaylandPointerTest, AxisHorizontal) { + wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_fixed_from_int(50), wl_fixed_from_int(75)); + wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_LEFT, + WL_POINTER_BUTTON_STATE_PRESSED); + + Sync(); + + scoped_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + // Wayland servers typically send a value of 10 per mouse wheel click. + wl_pointer_send_axis(pointer->resource(), 1003, + WL_POINTER_AXIS_HORIZONTAL_SCROLL, + wl_fixed_from_int(10)); + + Sync(); + + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseWheelEvent()); + auto mouse_wheel_event = static_cast<MouseWheelEvent*>(event.get()); + EXPECT_EQ(gfx::Vector2d(MouseWheelEvent::kWheelDelta, 0), + mouse_wheel_event->offset()); + EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, mouse_wheel_event->button_flags()); + EXPECT_EQ(0, mouse_wheel_event->changed_button_flags()); + EXPECT_EQ(gfx::PointF(50, 75), mouse_wheel_event->location_f()); + EXPECT_EQ(gfx::PointF(50, 75), mouse_wheel_event->root_location_f()); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_surface_factory.cc b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.cc new file mode 100644 index 00000000000..545016f7771 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.cc @@ -0,0 +1,170 @@ +// 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/wayland/wayland_surface_factory.h" + +#include <fcntl.h> +#include <sys/mman.h> +#include <wayland-client.h> + +#include "base/memory/shared_memory.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "ui/gfx/vsync_provider.h" +#include "ui/ozone/platform/wayland/wayland_display.h" +#include "ui/ozone/platform/wayland/wayland_object.h" +#include "ui/ozone/platform/wayland/wayland_window.h" +#include "ui/ozone/public/surface_ozone_canvas.h" +#include "ui/ozone/public/surface_ozone_egl.h" + +namespace ui { + +static void DeleteSharedMemory(void* pixels, void* context) { + delete static_cast<base::SharedMemory*>(context); +} + +class WaylandCanvasSurface : public SurfaceOzoneCanvas { + public: + WaylandCanvasSurface(WaylandDisplay* display, WaylandWindow* window_); + ~WaylandCanvasSurface() override; + + // SurfaceOzoneCanvas + skia::RefPtr<SkSurface> GetSurface() override; + void ResizeCanvas(const gfx::Size& viewport_size) override; + void PresentCanvas(const gfx::Rect& damage) override; + scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override; + + private: + WaylandDisplay* display_; + WaylandWindow* window_; + + gfx::Size size_; + skia::RefPtr<SkSurface> sk_surface_; + wl::Object<wl_shm_pool> pool_; + wl::Object<wl_buffer> buffer_; + + DISALLOW_COPY_AND_ASSIGN(WaylandCanvasSurface); +}; + +WaylandCanvasSurface::WaylandCanvasSurface(WaylandDisplay* display, + WaylandWindow* window) + : display_(display), window_(window), size_(window->GetBounds().size()) {} + +WaylandCanvasSurface::~WaylandCanvasSurface() {} + +skia::RefPtr<SkSurface> WaylandCanvasSurface::GetSurface() { + if (sk_surface_) + return sk_surface_; + + size_t length = size_.width() * size_.height() * 4; + auto shared_memory = make_scoped_ptr(new base::SharedMemory); + if (!shared_memory->CreateAndMapAnonymous(length)) + return nullptr; + + wl::Object<wl_shm_pool> pool( + wl_shm_create_pool(display_->shm(), shared_memory->handle().fd, length)); + if (!pool) + return nullptr; + wl::Object<wl_buffer> buffer( + wl_shm_pool_create_buffer(pool.get(), 0, size_.width(), size_.height(), + size_.width() * 4, WL_SHM_FORMAT_ARGB8888)); + if (!buffer) + return nullptr; + + sk_surface_ = skia::AdoptRef(SkSurface::NewRasterDirectReleaseProc( + SkImageInfo::MakeN32Premul(size_.width(), size_.height()), + shared_memory->memory(), size_.width() * 4, &DeleteSharedMemory, + shared_memory.get(), nullptr)); + if (!sk_surface_) + return nullptr; + pool_ = std::move(pool); + buffer_ = std::move(buffer); + (void)shared_memory.release(); + return sk_surface_; +} + +void WaylandCanvasSurface::ResizeCanvas(const gfx::Size& viewport_size) { + if (size_ == viewport_size) + return; + // TODO(forney): We could implement more efficient resizes by allocating + // buffers rounded up to larger sizes, and then reusing them if the new size + // still fits (but still reallocate if the new size is much smaller than the + // old size). + if (sk_surface_) { + sk_surface_.clear(); + buffer_.reset(); + pool_.reset(); + } + size_ = viewport_size; +} + +void WaylandCanvasSurface::PresentCanvas(const gfx::Rect& damage) { + // TODO(forney): This is just a naive implementation that allows chromium to + // draw to the buffer at any time, even if it is being used by the Wayland + // compositor. Instead, we should track buffer releases and frame callbacks + // from Wayland to ensure perfect frames (while minimizing copies). + wl_surface* surface = window_->surface(); + wl_surface_damage(surface, damage.x(), damage.y(), damage.width(), + damage.height()); + wl_surface_attach(surface, buffer_.get(), 0, 0); + wl_surface_commit(surface); + display_->ScheduleFlush(); +} + +scoped_ptr<gfx::VSyncProvider> WaylandCanvasSurface::CreateVSyncProvider() { + // TODO(forney): This can be implemented with information from frame + // callbacks, and possibly output refresh rate. + NOTIMPLEMENTED(); + return nullptr; +} + +WaylandSurfaceFactory::WaylandSurfaceFactory(WaylandDisplay* display) + : display_(display) {} + +WaylandSurfaceFactory::~WaylandSurfaceFactory() {} + +intptr_t WaylandSurfaceFactory::GetNativeDisplay() { + NOTIMPLEMENTED(); + return 0; +} + +bool WaylandSurfaceFactory::LoadEGLGLES2Bindings( + AddGLLibraryCallback add_gl_library, + SetGLGetProcAddressProcCallback set_gl_get_proc_address) { + // This Ozone implementation does not support multi-process rendering so + // disable EGL unconditionally for now. + return false; +} + +scoped_ptr<SurfaceOzoneCanvas> WaylandSurfaceFactory::CreateCanvasForWidget( + gfx::AcceleratedWidget widget) { + DCHECK(display_); + WaylandWindow* window = display_->GetWindow(widget); + DCHECK(window); + return make_scoped_ptr(new WaylandCanvasSurface(display_, window)); +} + +scoped_ptr<SurfaceOzoneEGL> WaylandSurfaceFactory::CreateEGLSurfaceForWidget( + gfx::AcceleratedWidget widget) { + NOTREACHED(); + return nullptr; +} + +scoped_refptr<NativePixmap> WaylandSurfaceFactory::CreateNativePixmap( + gfx::AcceleratedWidget widget, + gfx::Size size, + gfx::BufferFormat format, + gfx::BufferUsage usage) { + NOTIMPLEMENTED(); + return nullptr; +} + +scoped_refptr<NativePixmap> WaylandSurfaceFactory::CreateNativePixmapFromHandle( + gfx::Size size, + gfx::BufferFormat format, + const gfx::NativePixmapHandle& handle) { + NOTIMPLEMENTED(); + return nullptr; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_surface_factory.h b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.h new file mode 100644 index 00000000000..ce1793776ad --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.h @@ -0,0 +1,45 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SURFACE_FACTORY_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SURFACE_FACTORY_H_ + +#include "ui/ozone/public/surface_factory_ozone.h" + +namespace ui { + +class WaylandDisplay; + +class WaylandSurfaceFactory : public SurfaceFactoryOzone { + public: + explicit WaylandSurfaceFactory(WaylandDisplay* display); + ~WaylandSurfaceFactory() override; + + intptr_t GetNativeDisplay() override; + bool LoadEGLGLES2Bindings( + AddGLLibraryCallback add_gl_library, + SetGLGetProcAddressProcCallback set_gl_get_proc_address) override; + scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget( + gfx::AcceleratedWidget widget) override; + scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget( + gfx::AcceleratedWidget w) override; + scoped_refptr<NativePixmap> CreateNativePixmap( + gfx::AcceleratedWidget widget, + gfx::Size size, + gfx::BufferFormat format, + gfx::BufferUsage usage) override; + scoped_refptr<NativePixmap> CreateNativePixmapFromHandle( + gfx::Size size, + gfx::BufferFormat format, + const gfx::NativePixmapHandle& handle) override; + + private: + WaylandDisplay* display_; + + DISALLOW_COPY_AND_ASSIGN(WaylandSurfaceFactory); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SURFACE_FACTORY_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc new file mode 100644 index 00000000000..d2ef3fd95d3 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc @@ -0,0 +1,83 @@ +// 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 "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "ui/ozone/platform/wayland/fake_server.h" +#include "ui/ozone/platform/wayland/mock_platform_window_delegate.h" +#include "ui/ozone/platform/wayland/wayland_surface_factory.h" +#include "ui/ozone/platform/wayland/wayland_test.h" +#include "ui/ozone/platform/wayland/wayland_window.h" +#include "ui/ozone/public/surface_ozone_canvas.h" + +using ::testing::Expectation; +using ::testing::SaveArg; +using ::testing::_; + +namespace ui { + +class WaylandSurfaceFactoryTest : public WaylandTest { + public: + WaylandSurfaceFactoryTest() : surface_factory(&display) {} + + ~WaylandSurfaceFactoryTest() override {} + + void SetUp() override { + WaylandTest::SetUp(); + canvas = surface_factory.CreateCanvasForWidget(widget); + ASSERT_TRUE(canvas); + } + + protected: + WaylandSurfaceFactory surface_factory; + scoped_ptr<SurfaceOzoneCanvas> canvas; + + private: + DISALLOW_COPY_AND_ASSIGN(WaylandSurfaceFactoryTest); +}; + +TEST_F(WaylandSurfaceFactoryTest, Canvas) { + canvas->GetSurface(); + canvas->PresentCanvas(gfx::Rect(5, 10, 20, 15)); + + Expectation damage = EXPECT_CALL(*surface, Damage(5, 10, 20, 15)); + wl_resource* buffer_resource = nullptr; + Expectation attach = EXPECT_CALL(*surface, Attach(_, 0, 0)) + .WillOnce(SaveArg<0>(&buffer_resource)); + EXPECT_CALL(*surface, Commit()).After(damage, attach); + + Sync(); + + ASSERT_TRUE(buffer_resource); + wl_shm_buffer* buffer = wl_shm_buffer_get(buffer_resource); + ASSERT_TRUE(buffer); + EXPECT_EQ(wl_shm_buffer_get_width(buffer), 800); + EXPECT_EQ(wl_shm_buffer_get_height(buffer), 600); + + // TODO(forney): We could check that the contents match something drawn to the + // SkSurface above. +} + +TEST_F(WaylandSurfaceFactoryTest, CanvasResize) { + canvas->GetSurface(); + canvas->ResizeCanvas(gfx::Size(100, 50)); + canvas->GetSurface(); + canvas->PresentCanvas(gfx::Rect(0, 0, 100, 50)); + + Expectation damage = EXPECT_CALL(*surface, Damage(0, 0, 100, 50)); + wl_resource* buffer_resource = nullptr; + Expectation attach = EXPECT_CALL(*surface, Attach(_, 0, 0)) + .WillOnce(SaveArg<0>(&buffer_resource)); + EXPECT_CALL(*surface, Commit()).After(damage, attach); + + Sync(); + + ASSERT_TRUE(buffer_resource); + wl_shm_buffer* buffer = wl_shm_buffer_get(buffer_resource); + ASSERT_TRUE(buffer); + EXPECT_EQ(wl_shm_buffer_get_width(buffer), 100); + EXPECT_EQ(wl_shm_buffer_get_height(buffer), 50); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_test.cc b/chromium/ui/ozone/platform/wayland/wayland_test.cc new file mode 100644 index 00000000000..b5aea4d4a52 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_test.cc @@ -0,0 +1,56 @@ +// 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/wayland/wayland_test.h" + +#include "base/run_loop.h" + +using ::testing::SaveArg; +using ::testing::_; + +namespace ui { + +WaylandTest::WaylandTest() + : window(&delegate, &display, gfx::Rect(0, 0, 800, 600)) {} + +WaylandTest::~WaylandTest() {} + +void WaylandTest::SetUp() { + ASSERT_TRUE(server.Start()); + ASSERT_TRUE(display.Initialize()); + EXPECT_CALL(delegate, OnAcceleratedWidgetAvailable(_, _)) + .WillOnce(SaveArg<0>(&widget)); + ASSERT_TRUE(window.Initialize()); + ASSERT_NE(widget, gfx::kNullAcceleratedWidget); + + // Wait for the client to flush all pending requests from initialization. + base::RunLoop().RunUntilIdle(); + + // Pause the server after it has responded to all incoming events. + server.Pause(); + + surface = server.GetObject<wl::MockSurface>(widget); + ASSERT_TRUE(surface); + + initialized = true; +} + +void WaylandTest::TearDown() { + if (initialized) + Sync(); +} + +void WaylandTest::Sync() { + // Resume the server, flushing its pending events. + server.Resume(); + + // Wait for the client to finish processing these events. + base::RunLoop().RunUntilIdle(); + + // Pause the server, after it has finished processing any follow-up requests + // from the client. + server.Pause(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_test.h b/chromium/ui/ozone/platform/wayland/wayland_test.h new file mode 100644 index 00000000000..9a12412982a --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_test.h @@ -0,0 +1,47 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_TEST_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_TEST_H_ + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/ozone/platform/wayland/fake_server.h" +#include "ui/ozone/platform/wayland/mock_platform_window_delegate.h" +#include "ui/ozone/platform/wayland/wayland_display.h" +#include "ui/ozone/platform/wayland/wayland_window.h" + +namespace ui { + +// WaylandTest is a base class that sets up a display, window, and fake server, +// and allows easy synchronization between them. +class WaylandTest : public testing::Test { + public: + WaylandTest(); + ~WaylandTest() override; + + void SetUp() override; + void TearDown() override; + + void Sync(); + + private: + bool initialized = false; + base::MessageLoopForUI message_loop; + + protected: + wl::FakeServer server; + wl::MockSurface* surface; + + WaylandDisplay display; + MockPlatformWindowDelegate delegate; + WaylandWindow window; + gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + + private: + DISALLOW_COPY_AND_ASSIGN(WaylandTest); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_TEST_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_window.cc b/chromium/ui/ozone/platform/wayland/wayland_window.cc new file mode 100644 index 00000000000..46c639adc04 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_window.cc @@ -0,0 +1,183 @@ +// 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/wayland/wayland_window.h" + +#include <xdg-shell-unstable-v5-client-protocol.h> + +#include "base/strings/utf_string_conversions.h" +#include "ui/events/event.h" +#include "ui/events/ozone/events_ozone.h" +#include "ui/ozone/platform/wayland/wayland_display.h" +#include "ui/platform_window/platform_window_delegate.h" + +namespace ui { + +WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, + WaylandDisplay* display, + const gfx::Rect& bounds) + : delegate_(delegate), display_(display), bounds_(bounds) {} + +WaylandWindow::~WaylandWindow() { + if (xdg_surface_) { + PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); + display_->RemoveWindow(surface_.id()); + } +} + +// static +WaylandWindow* WaylandWindow::FromSurface(wl_surface* surface) { + return static_cast<WaylandWindow*>( + wl_proxy_get_user_data(reinterpret_cast<wl_proxy*>(surface))); +} + +bool WaylandWindow::Initialize() { + static const xdg_surface_listener xdg_surface_listener = { + &WaylandWindow::Configure, &WaylandWindow::Close, + }; + + surface_.reset(wl_compositor_create_surface(display_->compositor())); + if (!surface_) { + LOG(ERROR) << "Failed to create wl_surface"; + return false; + } + wl_surface_set_user_data(surface_.get(), this); + xdg_surface_.reset( + xdg_shell_get_xdg_surface(display_->shell(), surface_.get())); + if (!xdg_surface_) { + LOG(ERROR) << "Failed to create xdg_surface"; + return false; + } + xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this); + display_->ScheduleFlush(); + + display_->AddWindow(surface_.id(), this); + PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); + delegate_->OnAcceleratedWidgetAvailable(surface_.id(), 1.f); + + return true; +} + +void WaylandWindow::ApplyPendingBounds() { + if (pending_bounds_.IsEmpty()) + return; + + SetBounds(pending_bounds_); + DCHECK(xdg_surface_); + xdg_surface_ack_configure(xdg_surface_.get(), pending_configure_serial_); + pending_bounds_ = gfx::Rect(); + display_->ScheduleFlush(); +} + +void WaylandWindow::Show() {} + +void WaylandWindow::Hide() { + NOTIMPLEMENTED(); +} + +void WaylandWindow::Close() { + NOTIMPLEMENTED(); +} + +void WaylandWindow::SetBounds(const gfx::Rect& bounds) { + if (bounds == bounds_) + return; + bounds_ = bounds; + delegate_->OnBoundsChanged(bounds); +} + +gfx::Rect WaylandWindow::GetBounds() { + return bounds_; +} + +void WaylandWindow::SetTitle(const base::string16& title) { + DCHECK(xdg_surface_); + xdg_surface_set_title(xdg_surface_.get(), UTF16ToUTF8(title).c_str()); + display_->ScheduleFlush(); +} + +void WaylandWindow::SetCapture() { + NOTIMPLEMENTED(); +} + +void WaylandWindow::ReleaseCapture() { + NOTIMPLEMENTED(); +} + +void WaylandWindow::ToggleFullscreen() { + NOTIMPLEMENTED(); +} + +void WaylandWindow::Maximize() { + DCHECK(xdg_surface_); + xdg_surface_set_maximized(xdg_surface_.get()); + display_->ScheduleFlush(); +} + +void WaylandWindow::Minimize() { + DCHECK(xdg_surface_); + xdg_surface_set_minimized(xdg_surface_.get()); + display_->ScheduleFlush(); +} + +void WaylandWindow::Restore() { + DCHECK(xdg_surface_); + xdg_surface_unset_maximized(xdg_surface_.get()); + display_->ScheduleFlush(); +} + +void WaylandWindow::SetCursor(PlatformCursor cursor) { + NOTIMPLEMENTED(); +} + +void WaylandWindow::MoveCursorTo(const gfx::Point& location) { + NOTIMPLEMENTED(); +} + +void WaylandWindow::ConfineCursorToBounds(const gfx::Rect& bounds) { + NOTIMPLEMENTED(); +} + +PlatformImeController* WaylandWindow::GetPlatformImeController() { + NOTIMPLEMENTED(); + return nullptr; +} + +bool WaylandWindow::CanDispatchEvent(const PlatformEvent& native_event) { + Event* event = static_cast<Event*>(native_event); + if (event->IsMouseEvent()) + return has_pointer_focus_; + return false; +} + +uint32_t WaylandWindow::DispatchEvent(const PlatformEvent& native_event) { + DispatchEventFromNativeUiEvent( + native_event, base::Bind(&PlatformWindowDelegate::DispatchEvent, + base::Unretained(delegate_))); + return POST_DISPATCH_STOP_PROPAGATION; +} + +// static +void WaylandWindow::Configure(void* data, + xdg_surface* obj, + int32_t width, + int32_t height, + wl_array* states, + uint32_t serial) { + WaylandWindow* window = static_cast<WaylandWindow*>(data); + + // Rather than call SetBounds here for every configure event, just save the + // most recent bounds, and have WaylandDisplay call ApplyPendingBounds when it + // has finished processing events. We may get many configure events in a row + // during an interactive resize, and only the last one matters. + window->pending_bounds_ = gfx::Rect(0, 0, width, height); + window->pending_configure_serial_ = serial; +} + +// static +void WaylandWindow::Close(void* data, xdg_surface* obj) { + NOTIMPLEMENTED(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_window.h b/chromium/ui/ozone/platform/wayland/wayland_window.h new file mode 100644 index 00000000000..450df55d05a --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_window.h @@ -0,0 +1,86 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_WINDOW_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_WINDOW_H_ + +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/wayland_object.h" +#include "ui/platform_window/platform_window.h" + +namespace ui { + +class WaylandDisplay; + +class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { + public: + WaylandWindow(PlatformWindowDelegate* delegate, + WaylandDisplay* display, + const gfx::Rect& bounds); + ~WaylandWindow() override; + + static WaylandWindow* FromSurface(wl_surface* surface); + + bool Initialize(); + + wl_surface* surface() { return surface_.get(); } + + // Apply the bounds specified in the most recent configure event. This should + // be called after processing all pending events in the wayland connection. + void ApplyPendingBounds(); + + // Set whether this window has pointer focus and should dispatch mouse events. + void set_pointer_focus(bool focus) { has_pointer_focus_ = focus; } + + // PlatformWindow + void Show() override; + void Hide() override; + void Close() override; + void SetBounds(const gfx::Rect& bounds) override; + gfx::Rect GetBounds() override; + void SetTitle(const base::string16& title) override; + void SetCapture() override; + void ReleaseCapture() override; + void ToggleFullscreen() override; + void Maximize() override; + void Minimize() override; + void Restore() override; + void SetCursor(PlatformCursor cursor) override; + void MoveCursorTo(const gfx::Point& location) override; + void ConfineCursorToBounds(const gfx::Rect& bounds) override; + PlatformImeController* GetPlatformImeController() override; + + // PlatformEventDispatcher + bool CanDispatchEvent(const PlatformEvent& event) override; + uint32_t DispatchEvent(const PlatformEvent& event) override; + + // xdg_surface_listener + static void Configure(void* data, + xdg_surface* obj, + int32_t width, + int32_t height, + wl_array* states, + uint32_t serial); + static void Close(void* data, xdg_surface* obj); + + private: + PlatformWindowDelegate* delegate_; + WaylandDisplay* display_; + + wl::Object<wl_surface> surface_; + wl::Object<xdg_surface> xdg_surface_; + + gfx::Rect bounds_; + gfx::Rect pending_bounds_; + uint32_t pending_configure_serial_; + bool has_pointer_focus_ = false; + + DISALLOW_COPY_AND_ASSIGN(WaylandWindow); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_WINDOW_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc new file mode 100644 index 00000000000..5fd1dc337c0 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc @@ -0,0 +1,117 @@ +// 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 <wayland-server-core.h> +#include <xdg-shell-unstable-v5-server-protocol.h> + +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" +#include "ui/ozone/platform/wayland/fake_server.h" +#include "ui/ozone/platform/wayland/mock_platform_window_delegate.h" +#include "ui/ozone/platform/wayland/wayland_test.h" +#include "ui/ozone/platform/wayland/wayland_window.h" + +using ::testing::Eq; +using ::testing::Mock; +using ::testing::SaveArg; +using ::testing::StrEq; +using ::testing::_; + +namespace ui { + +class WaylandWindowTest : public WaylandTest { + public: + WaylandWindowTest() + : test_mouse_event(ET_MOUSE_PRESSED, + gfx::Point(10, 15), + gfx::Point(10, 15), + base::TimeDelta::FromSeconds(123456), + EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, + EF_LEFT_MOUSE_BUTTON) {} + + void SetUp() override { + WaylandTest::SetUp(); + + xdg_surface = surface->xdg_surface.get(); + ASSERT_TRUE(xdg_surface); + } + + protected: + wl::MockXdgSurface* xdg_surface; + + MouseEvent test_mouse_event; + + private: + DISALLOW_COPY_AND_ASSIGN(WaylandWindowTest); +}; + +TEST_F(WaylandWindowTest, SetTitle) { + EXPECT_CALL(*xdg_surface, SetTitle(StrEq("hello"))); + window.SetTitle(base::ASCIIToUTF16("hello")); +} + +TEST_F(WaylandWindowTest, Maximize) { + EXPECT_CALL(*xdg_surface, SetMaximized()); + window.Maximize(); +} + +TEST_F(WaylandWindowTest, Minimize) { + EXPECT_CALL(*xdg_surface, SetMinimized()); + window.Minimize(); +} + +TEST_F(WaylandWindowTest, Restore) { + EXPECT_CALL(*xdg_surface, UnsetMaximized()); + window.Restore(); +} + +TEST_F(WaylandWindowTest, CanDispatchMouseEventDefault) { + EXPECT_FALSE(window.CanDispatchEvent(&test_mouse_event)); +} + +TEST_F(WaylandWindowTest, CanDispatchMouseEventFocus) { + window.set_pointer_focus(true); + EXPECT_TRUE(window.CanDispatchEvent(&test_mouse_event)); +} + +TEST_F(WaylandWindowTest, CanDispatchMouseEventUnfocus) { + window.set_pointer_focus(false); + EXPECT_FALSE(window.CanDispatchEvent(&test_mouse_event)); +} + +ACTION_P(CloneEvent, ptr) { + *ptr = Event::Clone(*arg0); +} + +TEST_F(WaylandWindowTest, DispatchEvent) { + scoped_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + window.DispatchEvent(&test_mouse_event); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + auto mouse_event = static_cast<MouseEvent*>(event.get()); + EXPECT_EQ(mouse_event->location_f(), test_mouse_event.location_f()); + EXPECT_EQ(mouse_event->root_location_f(), test_mouse_event.root_location_f()); + EXPECT_EQ(mouse_event->time_stamp(), test_mouse_event.time_stamp()); + EXPECT_EQ(mouse_event->button_flags(), test_mouse_event.button_flags()); + EXPECT_EQ(mouse_event->changed_button_flags(), + test_mouse_event.changed_button_flags()); +} + +TEST_F(WaylandWindowTest, ConfigureEvent) { + wl_array states; + wl_array_init(&states); + xdg_surface_send_configure(xdg_surface->resource(), 1000, 1000, &states, 12); + xdg_surface_send_configure(xdg_surface->resource(), 1500, 1000, &states, 13); + + // Make sure that the implementation does not call OnBoundsChanged for each + // configure event if it receives multiple in a row. + EXPECT_CALL(delegate, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000)))); + EXPECT_CALL(*xdg_surface, AckConfigure(13)); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/BUILD.gn b/chromium/ui/ozone/platform/x11/BUILD.gn new file mode 100644 index 00000000000..31185ddbe55 --- /dev/null +++ b/chromium/ui/ozone/platform/x11/BUILD.gn @@ -0,0 +1,42 @@ +# 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. + +visibility = [ "//ui/ozone/*" ] + +source_set("x11") { + sources = [ + "client_native_pixmap_factory_x11.cc", + "client_native_pixmap_factory_x11.h", + "ozone_platform_x11.cc", + "ozone_platform_x11.h", + "x11_cursor_factory_ozone.cc", + "x11_cursor_factory_ozone.h", + "x11_surface_factory.cc", + "x11_surface_factory.h", + ] + + deps = [ + "//base", + "//skia", + "//ui/base", + "//ui/display/types", + "//ui/display/util", + "//ui/events", + "//ui/events/devices", + "//ui/events/ozone:events_ozone", + "//ui/events/platform", + "//ui/events/platform/x11", + "//ui/events/x", + "//ui/gfx", + "//ui/gfx/geometry", + "//ui/gfx/x", + "//ui/ozone:ozone_base", + "//ui/ozone/common", + "//ui/platform_window/x11", + ] + + configs += [ "//build/config/linux:x11" ] + + public_configs = [ "//third_party/khronos:khronos_headers" ] +} diff --git a/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.cc b/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.cc new file mode 100644 index 00000000000..b9083e51d91 --- /dev/null +++ b/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.cc @@ -0,0 +1,15 @@ +// 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/x11/client_native_pixmap_factory_x11.h" + +#include "ui/ozone/common/stub_client_native_pixmap_factory.h" + +namespace ui { + +ClientNativePixmapFactory* CreateClientNativePixmapFactoryX11() { + return CreateStubClientNativePixmapFactory(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.h b/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.h new file mode 100644 index 00000000000..b6523dd16a1 --- /dev/null +++ b/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.h @@ -0,0 +1,17 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_X11_CLIENT_NATIVE_PIXMAP_FACTORY_X11_H_ +#define UI_OZONE_PLATFORM_X11_CLIENT_NATIVE_PIXMAP_FACTORY_X11_H_ + +namespace ui { + +class ClientNativePixmapFactory; + +// Constructor hook for use in constructor_list.cc +ClientNativePixmapFactory* CreateClientNativePixmapFactoryX11(); + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_CLIENT_NATIVE_PIXMAP_FACTORY_X11_H_ diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc new file mode 100644 index 00000000000..7863f8001e9 --- /dev/null +++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -0,0 +1,129 @@ +// 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/x11/ozone_platform_x11.h" + +#include <X11/Xlib.h> + +#include <utility> + +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "ui/events/platform/x11/x11_event_source_libevent.h" +#include "ui/ozone/common/native_display_delegate_ozone.h" +#include "ui/ozone/common/stub_overlay_manager.h" +#include "ui/ozone/platform/x11/x11_cursor_factory_ozone.h" +#include "ui/ozone/platform/x11/x11_surface_factory.h" +#include "ui/ozone/public/gpu_platform_support.h" +#include "ui/ozone/public/gpu_platform_support_host.h" +#include "ui/ozone/public/input_controller.h" +#include "ui/ozone/public/ozone_platform.h" +#include "ui/ozone/public/system_input_injector.h" +#include "ui/platform_window/platform_window.h" +#include "ui/platform_window/x11/x11_window_manager_ozone.h" +#include "ui/platform_window/x11/x11_window_ozone.h" + +namespace ui { + +namespace { + +// Returns true if we should operate in Mus mode. +bool RunningInsideMus() { + bool has_channel_handle = base::CommandLine::ForCurrentProcess()->HasSwitch( + "mojo-platform-channel-handle"); + return has_channel_handle; +} + +// Singleton OzonePlatform implementation for Linux X11 platform. +class OzonePlatformX11 : public OzonePlatform { + public: + OzonePlatformX11() { + // If we're running in Mus mode both the UI and GPU components of Ozone will + // be running in the same process. Enable X11 concurrent thread support. + if (RunningInsideMus()) + XInitThreads(); + } + + ~OzonePlatformX11() override {} + + // OzonePlatform: + ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override { + return surface_factory_ozone_.get(); + } + + ui::OverlayManagerOzone* GetOverlayManager() override { + return overlay_manager_.get(); + } + + CursorFactoryOzone* GetCursorFactoryOzone() override { + return cursor_factory_ozone_.get(); + } + + scoped_ptr<SystemInputInjector> CreateSystemInputInjector() override { + return nullptr; + } + + InputController* GetInputController() override { + return input_controller_.get(); + } + + GpuPlatformSupport* GetGpuPlatformSupport() override { + return gpu_platform_support_.get(); + } + + GpuPlatformSupportHost* GetGpuPlatformSupportHost() override { + return gpu_platform_support_host_.get(); + } + + scoped_ptr<PlatformWindow> CreatePlatformWindow( + PlatformWindowDelegate* delegate, + const gfx::Rect& bounds) override { + scoped_ptr<X11WindowOzone> window = make_scoped_ptr(new X11WindowOzone( + event_source_.get(), window_manager_.get(), delegate)); + window->SetBounds(bounds); + window->Create(); + return std::move(window); + } + + scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override { + return make_scoped_ptr(new NativeDisplayDelegateOzone()); + } + + void InitializeUI() override { + window_manager_.reset(new X11WindowManagerOzone); + event_source_.reset(new X11EventSourceLibevent(gfx::GetXDisplay())); + overlay_manager_.reset(new StubOverlayManager()); + input_controller_ = CreateStubInputController(); + cursor_factory_ozone_.reset(new X11CursorFactoryOzone()); + gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); + } + + void InitializeGPU() override { + surface_factory_ozone_.reset(new X11SurfaceFactory()); + gpu_platform_support_.reset(CreateStubGpuPlatformSupport()); + } + + private: + // Objects in the Browser process. + scoped_ptr<X11WindowManagerOzone> window_manager_; + scoped_ptr<X11EventSourceLibevent> event_source_; + scoped_ptr<OverlayManagerOzone> overlay_manager_; + scoped_ptr<InputController> input_controller_; + scoped_ptr<X11CursorFactoryOzone> cursor_factory_ozone_; + scoped_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; + + // Objects in the GPU process. + scoped_ptr<X11SurfaceFactory> surface_factory_ozone_; + scoped_ptr<GpuPlatformSupport> gpu_platform_support_; + + DISALLOW_COPY_AND_ASSIGN(OzonePlatformX11); +}; + +} // namespace + +OzonePlatform* CreateOzonePlatformX11() { + return new OzonePlatformX11; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.h b/chromium/ui/ozone/platform/x11/ozone_platform_x11.h new file mode 100644 index 00000000000..b1f28a4fc7e --- /dev/null +++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.h @@ -0,0 +1,17 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_X11_OZONE_PLATFORM_X11_H_ +#define UI_OZONE_PLATFORM_X11_OZONE_PLATFORM_X11_H_ + +namespace ui { + +class OzonePlatform; + +// Constructor hook for use in ozone_platform_list.cc +OzonePlatform* CreateOzonePlatformX11(); + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_OZONE_PLATFORM_X11_H_ diff --git a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.cc b/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.cc new file mode 100644 index 00000000000..50df6919fb4 --- /dev/null +++ b/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.cc @@ -0,0 +1,100 @@ +// 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/x11/x11_cursor_factory_ozone.h" + +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/cursor/cursors_aura.h" +#include "ui/gfx/geometry/point.h" + +namespace ui { + +namespace { + +X11CursorOzone* ToX11CursorOzone(PlatformCursor cursor) { + return static_cast<X11CursorOzone*>(cursor); +} + +PlatformCursor ToPlatformCursor(X11CursorOzone* cursor) { + return static_cast<PlatformCursor>(cursor); +} + +// Gets default aura cursor bitmap/hotspot and creates a X11CursorOzone with it. +scoped_refptr<X11CursorOzone> CreateAuraX11Cursor(int type) { + SkBitmap bitmap; + gfx::Point hotspot; + if (GetCursorBitmap(type, &bitmap, &hotspot)) { + return new X11CursorOzone(bitmap, hotspot); + } + return nullptr; +} + +} // namespace + +X11CursorFactoryOzone::X11CursorFactoryOzone() + : invisible_cursor_(X11CursorOzone::CreateInvisible()) {} + +X11CursorFactoryOzone::~X11CursorFactoryOzone() {} + +PlatformCursor X11CursorFactoryOzone::GetDefaultCursor(int type) { + return ToPlatformCursor(GetDefaultCursorInternal(type).get()); +} + +PlatformCursor X11CursorFactoryOzone::CreateImageCursor( + const SkBitmap& bitmap, + const gfx::Point& hotspot) { + // There is a problem with custom cursors that have no custom data. The + // resulting SkBitmap is empty and X crashes when creating a zero size cursor + // image. Return invisible cursor here instead. + if (bitmap.drawsNothing()) { + return ToPlatformCursor(invisible_cursor_.get()); + } + + X11CursorOzone* cursor = new X11CursorOzone(bitmap, hotspot); + cursor->AddRef(); + return ToPlatformCursor(cursor); +} + +PlatformCursor X11CursorFactoryOzone::CreateAnimatedCursor( + const std::vector<SkBitmap>& bitmaps, + const gfx::Point& hotspot, + int frame_delay_ms) { + X11CursorOzone* cursor = new X11CursorOzone(bitmaps, hotspot, frame_delay_ms); + cursor->AddRef(); + return ToPlatformCursor(cursor); +} + +void X11CursorFactoryOzone::RefImageCursor(PlatformCursor cursor) { + ToX11CursorOzone(cursor)->AddRef(); +} + +void X11CursorFactoryOzone::UnrefImageCursor(PlatformCursor cursor) { + ToX11CursorOzone(cursor)->Release(); +} + +scoped_refptr<X11CursorOzone> X11CursorFactoryOzone::GetDefaultCursorInternal( + int type) { + if (type == kCursorNone) + return invisible_cursor_; + + // TODO(kylechar): Use predefined X cursors here instead. + if (!default_cursors_.count(type)) { + // Loads the default aura cursor bitmap for cursor type. Falls back on + // pointer cursor then invisible cursor if this fails. + scoped_refptr<X11CursorOzone> cursor = CreateAuraX11Cursor(type); + if (!cursor.get()) { + if (type != kCursorPointer) { + cursor = GetDefaultCursorInternal(kCursorPointer); + } else { + NOTREACHED() << "Failed to load default cursor bitmap"; + } + } + default_cursors_[type] = cursor; + } + + // Returns owned default cursor for this type. + return default_cursors_[type]; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.h b/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.h new file mode 100644 index 00000000000..2f12ac1ea3d --- /dev/null +++ b/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.h @@ -0,0 +1,52 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_X11_X11_CURSOR_FACTORY_OZONE_H_ +#define UI_OZONE_PLATFORM_X11_X11_CURSOR_FACTORY_OZONE_H_ + +#include <X11/X.h> + +#include <unordered_map> +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/base/cursor/cursor.h" +#include "ui/ozone/public/cursor_factory_ozone.h" +#include "ui/platform_window/x11/x11_cursor_ozone.h" + +namespace ui { + +// CursorFactoryOzone implementation for X11 cursors. +class X11CursorFactoryOzone : public CursorFactoryOzone { + public: + X11CursorFactoryOzone(); + ~X11CursorFactoryOzone() override; + + // CursorFactoryOzone: + PlatformCursor GetDefaultCursor(int type) override; + PlatformCursor CreateImageCursor(const SkBitmap& bitmap, + const gfx::Point& hotspot) override; + PlatformCursor CreateAnimatedCursor(const std::vector<SkBitmap>& bitmaps, + const gfx::Point& hotspot, + int frame_delay_ms) override; + void RefImageCursor(PlatformCursor cursor) override; + void UnrefImageCursor(PlatformCursor cursor) override; + + private: + // Loads/caches default cursor or returns cached version. + scoped_refptr<X11CursorOzone> GetDefaultCursorInternal(int type); + + // Holds a single instance of the invisible cursor. X11 has no way to hide + // the cursor so an invisible cursor mimics that. + scoped_refptr<X11CursorOzone> invisible_cursor_; + + std::unordered_map<int, scoped_refptr<X11CursorOzone>> default_cursors_; + + DISALLOW_COPY_AND_ASSIGN(X11CursorFactoryOzone); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_CURSOR_FACTORY_OZONE_H_ diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc new file mode 100644 index 00000000000..4e96c9b48d3 --- /dev/null +++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc @@ -0,0 +1,133 @@ +// 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/x11/x11_surface_factory.h" + +#include <X11/Xlib.h> + +#include "base/macros.h" +#include "third_party/khronos/EGL/egl.h" +#include "ui/gfx/vsync_provider.h" +#include "ui/gfx/x/x11_types.h" +#include "ui/ozone/common/egl_util.h" +#include "ui/ozone/public/surface_ozone_egl.h" + +namespace ui { + +namespace { + +class X11SurfaceEGL : public SurfaceOzoneEGL { + public: + explicit X11SurfaceEGL(gfx::AcceleratedWidget widget) : widget_(widget) {} + ~X11SurfaceEGL() override {} + + intptr_t GetNativeWindow() override { return widget_; } + + bool OnSwapBuffers() override { return true; } + + void OnSwapBuffersAsync(const SwapCompletionCallback& callback) override { + NOTREACHED(); + } + + bool ResizeNativeWindow(const gfx::Size& viewport_size) override { + return true; + } + + scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() override { + return nullptr; + } + + void* /* EGLConfig */ GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) override; + + private: + gfx::AcceleratedWidget widget_; + + DISALLOW_COPY_AND_ASSIGN(X11SurfaceEGL); +}; + +void* /* EGLConfig */ X11SurfaceEGL::GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) { + // Try matching the window depth with an alpha channel, + // because we're worried the destination alpha width could + // constrain blending precision. + EGLConfig config; + const int kBufferSizeOffset = 1; + const int kAlphaSizeOffset = 3; + EGLint config_attribs[] = {EGL_BUFFER_SIZE, + ~0, // To be replaced. + EGL_ALPHA_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_RED_SIZE, + 8, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT, + EGL_NONE}; + + // Get the depth of XWindow for surface + XWindowAttributes win_attribs; + if (XGetWindowAttributes(gfx::GetXDisplay(), widget_, &win_attribs)) { + config_attribs[kBufferSizeOffset] = win_attribs.depth; + } + + EGLint num_configs; + if (!egl.choose_config.Run(config_attribs, &config, 1, &num_configs)) { + LOG(ERROR) << "eglChooseConfig failed with error " + << egl.get_last_error_string.Run(); + return nullptr; + } + if (num_configs > 0) { + EGLint config_depth; + if (!egl.get_config_attribute.Run(config, EGL_BUFFER_SIZE, &config_depth)) { + LOG(ERROR) << "eglGetConfigAttrib failed with error " + << egl.get_last_error_string.Run(); + return nullptr; + } + if (config_depth == config_attribs[kBufferSizeOffset]) { + return config; + } + } + + // Try without an alpha channel. + config_attribs[kAlphaSizeOffset] = 0; + if (!egl.choose_config.Run(config_attribs, &config, 1, &num_configs)) { + LOG(ERROR) << "eglChooseConfig failed with error " + << egl.get_last_error_string.Run(); + return nullptr; + } + if (num_configs == 0) { + LOG(ERROR) << "No suitable EGL configs found."; + return nullptr; + } + return config; +} + +} // namespace + +X11SurfaceFactory::X11SurfaceFactory() {} + +X11SurfaceFactory::~X11SurfaceFactory() {} + +scoped_ptr<SurfaceOzoneEGL> X11SurfaceFactory::CreateEGLSurfaceForWidget( + gfx::AcceleratedWidget widget) { + return make_scoped_ptr(new X11SurfaceEGL(widget)); +} + +bool X11SurfaceFactory::LoadEGLGLES2Bindings( + AddGLLibraryCallback add_gl_library, + SetGLGetProcAddressProcCallback set_gl_get_proc_address) { + return LoadDefaultEGLGLES2Bindings(add_gl_library, set_gl_get_proc_address); +} + +intptr_t X11SurfaceFactory::GetNativeDisplay() { + return reinterpret_cast<intptr_t>(gfx::GetXDisplay()); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.h b/chromium/ui/ozone/platform/x11/x11_surface_factory.h new file mode 100644 index 00000000000..344704858ec --- /dev/null +++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.h @@ -0,0 +1,34 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_X11_X11_SURFACE_FACTORY_H_ +#define UI_OZONE_PLATFORM_X11_X11_SURFACE_FACTORY_H_ + +#include "base/memory/scoped_ptr.h" +#include "ui/ozone/public/surface_factory_ozone.h" + +namespace ui { + +// Handles creation of EGL and software surfaces for drawing in XWindow. +class X11SurfaceFactory : public SurfaceFactoryOzone { + public: + X11SurfaceFactory(); + ~X11SurfaceFactory() override; + + // SurfaceFactoryOzone: + + scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget( + gfx::AcceleratedWidget widget) override; + bool LoadEGLGLES2Bindings( + AddGLLibraryCallback add_gl_library, + SetGLGetProcAddressProcCallback set_gl_get_proc_address) override; + intptr_t GetNativeDisplay() override; + + private: + DISALLOW_COPY_AND_ASSIGN(X11SurfaceFactory); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_SURFACE_FACTORY_H_ diff --git a/chromium/ui/ozone/public/client_native_pixmap_factory.h b/chromium/ui/ozone/public/client_native_pixmap_factory.h index 9973bf6fef1..65aaefd7610 100644 --- a/chromium/ui/ozone/public/client_native_pixmap_factory.h +++ b/chromium/ui/ozone/public/client_native_pixmap_factory.h @@ -32,9 +32,6 @@ class OZONE_EXPORT ClientNativePixmapFactory { virtual ~ClientNativePixmapFactory(); - // Initialize with the given client native pixmap |device_fd|. - virtual void Initialize(base::ScopedFD device_fd) = 0; - // Returns true if format/usage configuration is supported. virtual bool IsConfigurationSupported(gfx::BufferFormat format, gfx::BufferUsage usage) const = 0; diff --git a/chromium/ui/ozone/public/overlay_candidates_ozone.cc b/chromium/ui/ozone/public/overlay_candidates_ozone.cc index 89777b7f843..d6fdc009edb 100644 --- a/chromium/ui/ozone/public/overlay_candidates_ozone.cc +++ b/chromium/ui/ozone/public/overlay_candidates_ozone.cc @@ -11,6 +11,9 @@ namespace ui { OverlayCandidatesOzone::OverlaySurfaceCandidate::OverlaySurfaceCandidate() : is_clipped(false) {} +OverlayCandidatesOzone::OverlaySurfaceCandidate::OverlaySurfaceCandidate( + const OverlaySurfaceCandidate& other) = default; + OverlayCandidatesOzone::OverlaySurfaceCandidate::~OverlaySurfaceCandidate() { } diff --git a/chromium/ui/ozone/public/overlay_candidates_ozone.h b/chromium/ui/ozone/public/overlay_candidates_ozone.h index cacf562a383..87e18ec93f5 100644 --- a/chromium/ui/ozone/public/overlay_candidates_ozone.h +++ b/chromium/ui/ozone/public/overlay_candidates_ozone.h @@ -20,6 +20,7 @@ class OZONE_BASE_EXPORT OverlayCandidatesOzone { public: struct OverlaySurfaceCandidate { OverlaySurfaceCandidate(); + OverlaySurfaceCandidate(const OverlaySurfaceCandidate& other); ~OverlaySurfaceCandidate(); // Transformation to apply to layer during composition. diff --git a/chromium/ui/ozone/public/ozone_platform.h b/chromium/ui/ozone/public/ozone_platform.h index f96915959b1..efa03a6cf10 100644 --- a/chromium/ui/ozone/public/ozone_platform.h +++ b/chromium/ui/ozone/public/ozone_platform.h @@ -5,7 +5,6 @@ #ifndef UI_OZONE_PUBLIC_OZONE_PLATFORM_H_ #define UI_OZONE_PUBLIC_OZONE_PLATFORM_H_ -#include "base/files/scoped_file.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "ui/ozone/ozone_export.h" @@ -70,9 +69,6 @@ class OZONE_EXPORT OzonePlatform { const gfx::Rect& bounds) = 0; virtual scoped_ptr<ui::NativeDisplayDelegate> CreateNativeDisplayDelegate() = 0; - // Open ClientNativePixmap device file for non-GPU processes to import a - // ClientNativePixmap. - virtual base::ScopedFD OpenClientNativePixmapDevice() const = 0; private: virtual void InitializeUI() = 0; diff --git a/chromium/ui/ozone/public/surface_factory_ozone.cc b/chromium/ui/ozone/public/surface_factory_ozone.cc index b493045353c..c8a28da16b4 100644 --- a/chromium/ui/ozone/public/surface_factory_ozone.cc +++ b/chromium/ui/ozone/public/surface_factory_ozone.cc @@ -39,9 +39,9 @@ scoped_ptr<SurfaceOzoneCanvas> SurfaceFactoryOzone::CreateCanvasForWidget( return nullptr; } -const int32_t* SurfaceFactoryOzone::GetEGLSurfaceProperties( - const int32_t* desired_attributes) { - return desired_attributes; +std::vector<gfx::BufferFormat> SurfaceFactoryOzone::GetScanoutFormats( + gfx::AcceleratedWidget widget) { + return std::vector<gfx::BufferFormat>(); } scoped_refptr<ui::NativePixmap> SurfaceFactoryOzone::CreateNativePixmap( @@ -54,6 +54,8 @@ scoped_refptr<ui::NativePixmap> SurfaceFactoryOzone::CreateNativePixmap( scoped_refptr<ui::NativePixmap> SurfaceFactoryOzone::CreateNativePixmapFromHandle( + gfx::Size size, + gfx::BufferFormat format, const gfx::NativePixmapHandle& handle) { return nullptr; } diff --git a/chromium/ui/ozone/public/surface_factory_ozone.h b/chromium/ui/ozone/public/surface_factory_ozone.h index eaa353b4bf8..cebf49a48ad 100644 --- a/chromium/ui/ozone/public/surface_factory_ozone.h +++ b/chromium/ui/ozone/public/surface_factory_ozone.h @@ -6,6 +6,7 @@ #define UI_OZONE_PUBLIC_SURFACE_FACTORY_OZONE_H_ #include <stdint.h> +#include <vector> #include "base/callback.h" #include "base/macros.h" @@ -41,8 +42,6 @@ class SurfaceOzoneEGL; // The following functions are specific to EGL: // - GetNativeDisplay // - LoadEGLGLES2Bindings -// - GetEGLSurfaceProperties (optional if the properties match the default -// Chromium ones). // - CreateEGLSurfaceForWidget // // 2) Software Drawing (Skia): @@ -93,12 +92,10 @@ class OZONE_BASE_EXPORT SurfaceFactoryOzone { AddGLLibraryCallback add_gl_library, SetGLGetProcAddressProcCallback set_gl_get_proc_address) = 0; - // Returns an array of EGL properties, which can be used in any EGL function - // used to select a display configuration. Note that all properties should be - // immediately followed by the corresponding desired value and array should be - // terminated with EGL_NONE. Ownership of the array is not transferred to - // caller. desired_list contains list of desired EGL properties and values. - virtual const int32_t* GetEGLSurfaceProperties(const int32_t* desired_list); + // Returns all scanout formats for |widget| representing a particular display + // controller or default display controller for kNullAcceleratedWidget. + virtual std::vector<gfx::BufferFormat> GetScanoutFormats( + gfx::AcceleratedWidget widget); // Create a single native buffer to be used for overlay planes or zero copy // for |widget| representing a particular display controller or default @@ -113,6 +110,8 @@ class OZONE_BASE_EXPORT SurfaceFactoryOzone { // Create a single native buffer from an existing handle. Takes ownership of // |handle| and can be called on any thread. virtual scoped_refptr<NativePixmap> CreateNativePixmapFromHandle( + gfx::Size size, + gfx::BufferFormat format, const gfx::NativePixmapHandle& handle); protected: diff --git a/chromium/ui/ozone/public/surface_ozone_canvas.h b/chromium/ui/ozone/public/surface_ozone_canvas.h index 6cab0b40f0e..31bada38f70 100644 --- a/chromium/ui/ozone/public/surface_ozone_canvas.h +++ b/chromium/ui/ozone/public/surface_ozone_canvas.h @@ -27,7 +27,7 @@ class OZONE_BASE_EXPORT SurfaceOzoneCanvas { virtual ~SurfaceOzoneCanvas() {} // Returns an SkSurface for drawing on the window. - virtual skia::RefPtr<SkSurface> GetSurface() = 0; + virtual sk_sp<SkSurface> GetSurface() = 0; // Attempts to resize the canvas to match the viewport size. After // resizing, the compositor must call GetCanvas() to get the next diff --git a/chromium/ui/ozone/public/surface_ozone_egl.cc b/chromium/ui/ozone/public/surface_ozone_egl.cc index 92330f3bf38..ba61f4f1482 100644 --- a/chromium/ui/ozone/public/surface_ozone_egl.cc +++ b/chromium/ui/ozone/public/surface_ozone_egl.cc @@ -6,6 +6,13 @@ namespace ui { +EglConfigCallbacks::EglConfigCallbacks() {} + +EglConfigCallbacks::EglConfigCallbacks(const EglConfigCallbacks& other) = + default; + +EglConfigCallbacks::~EglConfigCallbacks() {} + bool SurfaceOzoneEGL::IsUniversalDisplayLinkDevice() { return false; } diff --git a/chromium/ui/ozone/public/surface_ozone_egl.h b/chromium/ui/ozone/public/surface_ozone_egl.h index f66f354e50c..67424a4bcc8 100644 --- a/chromium/ui/ozone/public/surface_ozone_egl.h +++ b/chromium/ui/ozone/public/surface_ozone_egl.h @@ -21,6 +21,22 @@ class NativePixmap; typedef base::Callback<void(gfx::SwapResult)> SwapCompletionCallback; +// Holds callbacks to functions for configuring EGL on platform. +struct OZONE_BASE_EXPORT EglConfigCallbacks { + EglConfigCallbacks(); + EglConfigCallbacks(const EglConfigCallbacks& other); + ~EglConfigCallbacks(); + base::Callback<bool(const int32_t* attribs, + void** /* EGLConfig* */ configs, + int32_t config_size, + int32_t* num_configs)> + choose_config; + base::Callback< + bool(void* /* EGLConfig */ config, int32_t attribute, int32_t* value)> + get_config_attribute; + base::Callback<const char*()> get_last_error_string; +}; + // The platform-specific part of an EGL surface. // // This class owns any bits that the ozone implementation needs freed when @@ -56,6 +72,11 @@ class OZONE_BASE_EXPORT SurfaceOzoneEGL { // Returns true if the surface is created on a UDL device. virtual bool IsUniversalDisplayLinkDevice(); + + // Returns the EGL configuration to use for this surface. The default EGL + // configuration will be used if this returns nullptr. + virtual void* /* EGLConfig */ GetEGLSurfaceConfig( + const EglConfigCallbacks& egl) = 0; }; } // namespace ui diff --git a/chromium/ui/platform_window/BUILD.gn b/chromium/ui/platform_window/BUILD.gn index fe52aea4767..a9905a80fdc 100644 --- a/chromium/ui/platform_window/BUILD.gn +++ b/chromium/ui/platform_window/BUILD.gn @@ -16,7 +16,7 @@ source_set("platform_window") { deps = [ "//base", "//ui/base", - "//ui/base/ime", + "//ui/base/ime:text_input_types", "//ui/gfx", ] } diff --git a/chromium/ui/platform_window/stub/stub_window.gyp b/chromium/ui/platform_window/stub/stub_window.gyp new file mode 100644 index 00000000000..47422f10cf9 --- /dev/null +++ b/chromium/ui/platform_window/stub/stub_window.gyp @@ -0,0 +1,24 @@ +# 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [{ + 'target_name': 'stub_window', + 'type': '<(component)', + 'dependencies': [ + '../../gfx/gfx.gyp:gfx', + '../../gfx/gfx.gyp:gfx_geometry', + '../platform_window.gyp:platform_window', + ], + 'defines': [ 'STUB_WINDOW_IMPLEMENTATION' ], + 'sources': [ + 'stub_window.cc', + 'stub_window.h', + 'stub_window_export.h', + ], + }], +} diff --git a/chromium/ui/platform_window/text_input_state.cc b/chromium/ui/platform_window/text_input_state.cc index ce6db841857..ffc3e52792a 100644 --- a/chromium/ui/platform_window/text_input_state.cc +++ b/chromium/ui/platform_window/text_input_state.cc @@ -32,6 +32,8 @@ TextInputState::TextInputState(TextInputType type, composition_end(composition_end), can_compose_inline(can_compose_inline) {} +TextInputState::TextInputState(const TextInputState& other) = default; + bool TextInputState::operator==(const TextInputState& other) const { return type == other.type && flags == other.flags && diff --git a/chromium/ui/platform_window/text_input_state.h b/chromium/ui/platform_window/text_input_state.h index 14d016bcede..c2dff0dc96d 100644 --- a/chromium/ui/platform_window/text_input_state.h +++ b/chromium/ui/platform_window/text_input_state.h @@ -23,6 +23,7 @@ struct TextInputState { int composition_start, int composition_end, bool can_compose_inline); + TextInputState(const TextInputState& other); bool operator==(const TextInputState& other) const; // The type of input field. diff --git a/chromium/ui/platform_window/win/win_window.cc b/chromium/ui/platform_window/win/win_window.cc index 802f2d79a21..1282b091308 100644 --- a/chromium/ui/platform_window/win/win_window.cc +++ b/chromium/ui/platform_window/win/win_window.cc @@ -74,9 +74,11 @@ void WinWindow::SetBounds(const gfx::Rect& bounds) { GetWindowLong(hwnd(), GWL_STYLE), GetWindowLong(hwnd(), GWL_EXSTYLE), bounds); + unsigned int flags = SWP_NOREPOSITION; + if (!::IsWindowVisible(hwnd())) + flags |= SWP_NOACTIVATE; SetWindowPos(hwnd(), NULL, window_bounds.x(), window_bounds.y(), - window_bounds.width(), window_bounds.height(), - SWP_NOREPOSITION); + window_bounds.width(), window_bounds.height(), flags); } gfx::Rect WinWindow::GetBounds() { diff --git a/chromium/ui/platform_window/x11/BUILD.gn b/chromium/ui/platform_window/x11/BUILD.gn index 4775aa77144..bac1aaa2bae 100644 --- a/chromium/ui/platform_window/x11/BUILD.gn +++ b/chromium/ui/platform_window/x11/BUILD.gn @@ -3,8 +3,9 @@ # found in the LICENSE file. import("//build/config/ui.gni") +import("//ui/ozone/ozone.gni") -assert(use_x11) +assert(use_x11 || ozone_platform_x11) component("x11") { output_name = "x11_window" @@ -25,8 +26,25 @@ component("x11") { defines = [ "X11_WINDOW_IMPLEMENTATION" ] sources = [ - "x11_window.cc", - "x11_window.h", + "x11_window_base.cc", + "x11_window_base.h", "x11_window_export.h", ] + + if (ozone_platform_x11) { + sources += [ + "x11_cursor_ozone.cc", + "x11_cursor_ozone.h", + "x11_window_manager_ozone.cc", + "x11_window_manager_ozone.h", + "x11_window_ozone.cc", + "x11_window_ozone.h", + ] + deps += [ "//ui/base" ] + } else if (use_x11) { + sources += [ + "x11_window.cc", + "x11_window.h", + ] + } } diff --git a/chromium/ui/platform_window/x11/DEPS b/chromium/ui/platform_window/x11/DEPS index 8cb61f5e0a4..9e087417f97 100644 --- a/chromium/ui/platform_window/x11/DEPS +++ b/chromium/ui/platform_window/x11/DEPS @@ -1,4 +1,6 @@ include_rules = [ + "+third_party/skia/include", + "+ui/base/x", "+ui/events", "+ui/gfx", ] diff --git a/chromium/ui/platform_window/x11/x11_cursor_ozone.cc b/chromium/ui/platform_window/x11/x11_cursor_ozone.cc new file mode 100644 index 00000000000..ff185e37560 --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_cursor_ozone.cc @@ -0,0 +1,84 @@ +// 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/platform_window/x11/x11_cursor_ozone.h" + +#include <X11/Xcursor/Xcursor.h> +#include <X11/Xlib.h> + +#include "base/logging.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/geometry/point.h" + +namespace ui { + +namespace { + +// Converts a SKBitmap to unpremul alpha. +SkBitmap ConvertSkBitmapToUnpremul(const SkBitmap& bitmap) { + DCHECK_NE(bitmap.alphaType(), kUnpremul_SkAlphaType); + + SkImageInfo image_info = SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), + kUnpremul_SkAlphaType); + SkBitmap converted_bitmap; + converted_bitmap.allocPixels(image_info); + bitmap.readPixels(image_info, converted_bitmap.getPixels(), + image_info.minRowBytes(), 0, 0); + + return converted_bitmap; +} + +// Creates an XCursorImage for cursor bitmap. +XcursorImage* CreateXCursorImage(const SkBitmap& bitmap, + const gfx::Point& hotspot) { + // X11 expects bitmap with unpremul alpha. If bitmap is premul then convert, + // otherwise semi-transparent parts of cursor will look strange. + if (bitmap.alphaType() != kUnpremul_SkAlphaType) { + SkBitmap converted_bitmap = ConvertSkBitmapToUnpremul(bitmap); + return SkBitmapToXcursorImage(&converted_bitmap, hotspot); + } else { + return SkBitmapToXcursorImage(&bitmap, hotspot); + } +} + +} // namespace + +X11CursorOzone::X11CursorOzone(const SkBitmap& bitmap, + const gfx::Point& hotspot) { + XcursorImage* image = CreateXCursorImage(bitmap, hotspot); + xcursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image); + XcursorImageDestroy(image); +} + +X11CursorOzone::X11CursorOzone(const std::vector<SkBitmap>& bitmaps, + const gfx::Point& hotspot, + int frame_delay_ms) { + // Initialize an XCursorImage for each frame, store all of them in + // XCursorImages and load the cursor from that. + XcursorImages* images = XcursorImagesCreate(bitmaps.size()); + images->nimage = bitmaps.size(); + for (size_t frame = 0; frame < bitmaps.size(); ++frame) { + XcursorImage* x_image = CreateXCursorImage(bitmaps[frame], hotspot); + x_image->delay = frame_delay_ms; + images->images[frame] = x_image; + } + + xcursor_ = XcursorImagesLoadCursor(gfx::GetXDisplay(), images); + XcursorImagesDestroy(images); +} + +scoped_refptr<X11CursorOzone> X11CursorOzone::CreateInvisible() { + scoped_refptr<X11CursorOzone> invisible_ = new X11CursorOzone(); + invisible_->xcursor_ = CreateInvisibleCursor(); + return invisible_; +} + +X11CursorOzone::X11CursorOzone() {} + +X11CursorOzone::~X11CursorOzone() { + XFreeCursor(gfx::GetXDisplay(), xcursor_); +} + +} // namespace ui diff --git a/chromium/ui/platform_window/x11/x11_cursor_ozone.h b/chromium/ui/platform_window/x11/x11_cursor_ozone.h new file mode 100644 index 00000000000..8a83080e746 --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_cursor_ozone.h @@ -0,0 +1,48 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_PLATFORM_WINDOW_X11_X11_CURSOR_OZONE_H_ +#define UI_PLATFORM_WINDOW_X11_X11_CURSOR_OZONE_H_ + +#include <X11/X.h> + +#include <vector> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "ui/base/cursor/cursor.h" +#include "ui/platform_window/x11/x11_window_export.h" + +class SkBitmap; + +namespace ui { + +// Ref counted class to hold an X11 cursor resource. Handles creating X11 cursor +// resources from SkBitmap/hotspot and clears the X11 resources on destruction. +class X11_WINDOW_EXPORT X11CursorOzone + : public base::RefCounted<X11CursorOzone> { + public: + X11CursorOzone(const SkBitmap& bitmap, const gfx::Point& hotspot); + X11CursorOzone(const std::vector<SkBitmap>& bitmaps, + const gfx::Point& hotspot, + int frame_delay_ms); + + // Creates a new cursor that is invisible. + static scoped_refptr<X11CursorOzone> CreateInvisible(); + + ::Cursor xcursor() const { return xcursor_; } + + private: + friend class base::RefCounted<X11CursorOzone>; + + X11CursorOzone(); + ~X11CursorOzone(); + + ::Cursor xcursor_ = None; + + DISALLOW_COPY_AND_ASSIGN(X11CursorOzone); +}; + +} // namespace ui +#endif // UI_PLATFORM_WINDOW_X11_X11_CURSOR_OZONE_H_ diff --git a/chromium/ui/platform_window/x11/x11_window.cc b/chromium/ui/platform_window/x11/x11_window.cc index 6900a448915..f54b4166743 100644 --- a/chromium/ui/platform_window/x11/x11_window.cc +++ b/chromium/ui/platform_window/x11/x11_window.cc @@ -5,74 +5,30 @@ #include "ui/platform_window/x11/x11_window.h" #include <X11/extensions/XInput2.h> -#include <X11/Xatom.h> #include <X11/Xlib.h> #include <X11/Xutil.h> -#include "base/strings/utf_string_conversions.h" #include "ui/events/devices/x11/touch_factory_x11.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" -#include "ui/events/platform/platform_event_dispatcher.h" #include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/x11/x11_event_source.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/x/x11_atom_cache.h" -#include "ui/gfx/x/x11_types.h" #include "ui/platform_window/platform_window_delegate.h" namespace ui { -namespace { - -const char* kAtomsToCache[] = { - "UTF8_STRING", - "WM_DELETE_WINDOW", - "_NET_WM_NAME", - "_NET_WM_PID", - "_NET_WM_PING", - NULL -}; - -XID FindXEventTarget(XEvent* xevent) { - XID target = xevent->xany.window; - if (xevent->type == GenericEvent) - target = static_cast<XIDeviceEvent*>(xevent->xcookie.data)->event; - return target; -} - -bool g_override_redirect = false; - -} // namespace - X11Window::X11Window(PlatformWindowDelegate* delegate) - : delegate_(delegate), - xdisplay_(gfx::GetXDisplay()), - xwindow_(None), - xroot_window_(DefaultRootWindow(xdisplay_)), - atom_cache_(xdisplay_, kAtomsToCache), - window_mapped_(false) { - CHECK(delegate_); - TouchFactory::SetTouchDeviceListFromCommandLine(); + : X11WindowBase(delegate) { + DCHECK(PlatformEventSource::GetInstance()); + PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); } X11Window::~X11Window() { - Destroy(); -} - -void X11Window::Destroy() { - if (xwindow_ == None) - return; - - // Stop processing events. PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - XID xwindow = xwindow_; - XDisplay* xdisplay = xdisplay_; - xwindow_ = None; - delegate_->OnClosed(); - // |this| might be deleted because of the above call. +} - XDestroyWindow(xdisplay, xwindow); +void X11Window::SetCursor(PlatformCursor cursor) { + XDefineCursor(xdisplay(), xwindow(), cursor); } void X11Window::ProcessXInput2Event(XEvent* xev) { @@ -83,7 +39,7 @@ void X11Window::ProcessXInput2Event(XEvent* xev) { case ET_KEY_PRESSED: case ET_KEY_RELEASED: { KeyEvent key_event(xev); - delegate_->DispatchEvent(&key_event); + delegate()->DispatchEvent(&key_event); break; } case ET_MOUSE_PRESSED: @@ -91,19 +47,19 @@ void X11Window::ProcessXInput2Event(XEvent* xev) { case ET_MOUSE_DRAGGED: case ET_MOUSE_RELEASED: { MouseEvent mouse_event(xev); - delegate_->DispatchEvent(&mouse_event); + delegate()->DispatchEvent(&mouse_event); break; } case ET_MOUSEWHEEL: { MouseWheelEvent wheel_event(xev); - delegate_->DispatchEvent(&wheel_event); + delegate()->DispatchEvent(&wheel_event); break; } case ET_SCROLL_FLING_START: case ET_SCROLL_FLING_CANCEL: case ET_SCROLL: { ScrollEvent scroll_event(xev); - delegate_->DispatchEvent(&scroll_event); + delegate()->DispatchEvent(&scroll_event); break; } case ET_TOUCH_MOVED: @@ -111,7 +67,7 @@ void X11Window::ProcessXInput2Event(XEvent* xev) { case ET_TOUCH_CANCELLED: case ET_TOUCH_RELEASED: { TouchEvent touch_event(xev); - delegate_->DispatchEvent(&touch_event); + delegate()->DispatchEvent(&touch_event); break; } default: @@ -119,182 +75,8 @@ void X11Window::ProcessXInput2Event(XEvent* xev) { } } -void X11Window::Show() { - if (window_mapped_) - return; - - CHECK(PlatformEventSource::GetInstance()); - PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - - XSetWindowAttributes swa; - memset(&swa, 0, sizeof(swa)); - swa.background_pixmap = None; - swa.bit_gravity = NorthWestGravity; - swa.override_redirect = g_override_redirect; - xwindow_ = XCreateWindow(xdisplay_, - xroot_window_, - requested_bounds_.x(), - requested_bounds_.y(), - requested_bounds_.width(), - requested_bounds_.height(), - 0, // border width - CopyFromParent, // depth - InputOutput, - CopyFromParent, // visual - CWBackPixmap | CWBitGravity | CWOverrideRedirect, - &swa); - - long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | - KeyPressMask | KeyReleaseMask | EnterWindowMask | - LeaveWindowMask | ExposureMask | VisibilityChangeMask | - StructureNotifyMask | PropertyChangeMask | - PointerMotionMask; - XSelectInput(xdisplay_, xwindow_, event_mask); - - unsigned char mask[XIMaskLen(XI_LASTEVENT)]; - memset(mask, 0, sizeof(mask)); - - XISetMask(mask, XI_TouchBegin); - XISetMask(mask, XI_TouchUpdate); - XISetMask(mask, XI_TouchEnd); - XISetMask(mask, XI_ButtonPress); - XISetMask(mask, XI_ButtonRelease); - XISetMask(mask, XI_Motion); - XISetMask(mask, XI_KeyPress); - XISetMask(mask, XI_KeyRelease); - - XIEventMask evmask; - evmask.deviceid = XIAllDevices; - evmask.mask_len = sizeof(mask); - evmask.mask = mask; - XISelectEvents(xdisplay_, xwindow_, &evmask, 1); - XFlush(xdisplay_); - - ::Atom protocols[2]; - protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); - protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); - XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); - - // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with - // the desktop environment. - XSetWMProperties( - xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); - - // Likewise, the X server needs to know this window's pid so it knows which - // program to kill if the window hangs. - // XChangeProperty() expects "pid" to be long. - static_assert(sizeof(long) >= sizeof(pid_t), - "pid_t should not be larger than long"); - long pid = getpid(); - XChangeProperty(xdisplay_, - xwindow_, - atom_cache_.GetAtom("_NET_WM_PID"), - XA_CARDINAL, - 32, - PropModeReplace, - reinterpret_cast<unsigned char*>(&pid), - 1); - // Before we map the window, set size hints. Otherwise, some window managers - // will ignore toplevel XMoveWindow commands. - XSizeHints size_hints; - size_hints.flags = PPosition | PWinGravity; - size_hints.x = requested_bounds_.x(); - size_hints.y = requested_bounds_.y(); - // Set StaticGravity so that the window position is not affected by the - // frame width when running with window manager. - size_hints.win_gravity = StaticGravity; - XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); - - XMapWindow(xdisplay_, xwindow_); - - // We now block until our window is mapped. Some X11 APIs will crash and - // burn if passed |xwindow_| before the window is mapped, and XMapWindow is - // asynchronous. - if (X11EventSource::GetInstance()) - X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); - window_mapped_ = true; - - // TODO(sky): provide real scale factor. - delegate_->OnAcceleratedWidgetAvailable(xwindow_, 1.f); -} - -void X11Window::Hide() { - if (!window_mapped_) - return; - XWithdrawWindow(xdisplay_, xwindow_, 0); - window_mapped_ = false; -} - -void X11Window::Close() { - Destroy(); -} - -void X11Window::SetBounds(const gfx::Rect& bounds) { - requested_bounds_ = bounds; - if (!window_mapped_) - return; - XWindowChanges changes = {0}; - unsigned value_mask = CWX | CWY | CWWidth | CWHeight; - changes.x = bounds.x(); - changes.y = bounds.y(); - changes.width = bounds.width(); - changes.height = bounds.height(); - XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); -} - -gfx::Rect X11Window::GetBounds() { - return confirmed_bounds_; -} - -void X11Window::SetTitle(const base::string16& title) { - if (window_title_ == title) - return; - window_title_ = title; - std::string utf8str = base::UTF16ToUTF8(title); - XChangeProperty(xdisplay_, - xwindow_, - atom_cache_.GetAtom("_NET_WM_NAME"), - atom_cache_.GetAtom("UTF8_STRING"), - 8, - PropModeReplace, - reinterpret_cast<const unsigned char*>(utf8str.c_str()), - utf8str.size()); - XTextProperty xtp; - char *c_utf8_str = const_cast<char *>(utf8str.c_str()); - if (Xutf8TextListToTextProperty(xdisplay_, &c_utf8_str, 1, - XUTF8StringStyle, &xtp) == Success) { - XSetWMName(xdisplay_, xwindow_, &xtp); - XFree(xtp.value); - } -} - -void X11Window::SetCapture() {} - -void X11Window::ReleaseCapture() {} - -void X11Window::ToggleFullscreen() {} - -void X11Window::Maximize() {} - -void X11Window::Minimize() {} - -void X11Window::Restore() {} - -void X11Window::SetCursor(PlatformCursor cursor) { - XDefineCursor(xdisplay_, xwindow_, cursor); -} - -void X11Window::MoveCursorTo(const gfx::Point& location) {} - -void X11Window::ConfineCursorToBounds(const gfx::Rect& bounds) { -} - -PlatformImeController* X11Window::GetPlatformImeController() { - return nullptr; -} - -bool X11Window::CanDispatchEvent(const PlatformEvent& event) { - return FindXEventTarget(event) == xwindow_; +bool X11Window::CanDispatchEvent(const PlatformEvent& xev) { + return IsEventForXWindow(*xev); } uint32_t X11Window::DispatchEvent(const PlatformEvent& event) { @@ -306,28 +88,19 @@ uint32_t X11Window::DispatchEvent(const PlatformEvent& event) { MouseEvent mouse_event(xev); CHECK_EQ(ET_MOUSE_MOVED, mouse_event.type()); mouse_event.set_flags(mouse_event.flags() | EF_IS_SYNTHESIZED); - delegate_->DispatchEvent(&mouse_event); + delegate()->DispatchEvent(&mouse_event); break; } case LeaveNotify: { MouseEvent mouse_event(xev); - delegate_->DispatchEvent(&mouse_event); - break; - } - - case Expose: { - gfx::Rect damage_rect(xev->xexpose.x, - xev->xexpose.y, - xev->xexpose.width, - xev->xexpose.height); - delegate_->OnDamageRect(damage_rect); + delegate()->DispatchEvent(&mouse_event); break; } case KeyPress: case KeyRelease: { KeyEvent key_event(xev); - delegate_->DispatchEvent(&key_event); + delegate()->DispatchEvent(&key_event); break; } @@ -336,13 +109,13 @@ uint32_t X11Window::DispatchEvent(const PlatformEvent& event) { switch (EventTypeFromNative(xev)) { case ET_MOUSEWHEEL: { MouseWheelEvent mouseev(xev); - delegate_->DispatchEvent(&mouseev); + delegate()->DispatchEvent(&mouseev); break; } case ET_MOUSE_PRESSED: case ET_MOUSE_RELEASED: { MouseEvent mouseev(xev); - delegate_->DispatchEvent(&mouseev); + delegate()->DispatchEvent(&mouseev); break; } case ET_UNKNOWN: @@ -355,40 +128,11 @@ uint32_t X11Window::DispatchEvent(const PlatformEvent& event) { break; } + case Expose: case FocusOut: - if (xev->xfocus.mode != NotifyGrab) - delegate_->OnLostCapture(); - break; - - case ConfigureNotify: { - DCHECK_EQ(xwindow_, xev->xconfigure.event); - DCHECK_EQ(xwindow_, xev->xconfigure.window); - gfx::Rect bounds(xev->xconfigure.x, - xev->xconfigure.y, - xev->xconfigure.width, - xev->xconfigure.height); - if (confirmed_bounds_ != bounds) { - confirmed_bounds_ = bounds; - delegate_->OnBoundsChanged(confirmed_bounds_); - } - break; - } - + case ConfigureNotify: case ClientMessage: { - Atom message = static_cast<Atom>(xev->xclient.data.l[0]); - if (message == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { - delegate_->OnCloseRequest(); - } else if (message == atom_cache_.GetAtom("_NET_WM_PING")) { - XEvent reply_event = *xev; - reply_event.xclient.window = xroot_window_; - - XSendEvent(xdisplay_, - reply_event.xclient.window, - False, - SubstructureRedirectMask | SubstructureNotifyMask, - &reply_event); - XFlush(xdisplay_); - } + ProcessXWindowEvent(xev); break; } @@ -400,11 +144,4 @@ uint32_t X11Window::DispatchEvent(const PlatformEvent& event) { return POST_DISPATCH_STOP_PROPAGATION; } -namespace test { - -void SetUseOverrideRedirectWindowByDefault(bool override_redirect) { - g_override_redirect = override_redirect; -} - -} // namespace test } // namespace ui diff --git a/chromium/ui/platform_window/x11/x11_window.gyp b/chromium/ui/platform_window/x11/x11_window.gyp index 0dd840a5e2b..f4b948ae3f7 100644 --- a/chromium/ui/platform_window/x11/x11_window.gyp +++ b/chromium/ui/platform_window/x11/x11_window.gyp @@ -26,6 +26,8 @@ 'sources': [ 'x11_window.cc', 'x11_window.h', + 'x11_window_base.cc', + 'x11_window_base.h', 'x11_window_export.h', ], }], diff --git a/chromium/ui/platform_window/x11/x11_window.h b/chromium/ui/platform_window/x11/x11_window.h index 54bbed86082..c3f15cf9cdc 100644 --- a/chromium/ui/platform_window/x11/x11_window.h +++ b/chromium/ui/platform_window/x11/x11_window.h @@ -5,86 +5,33 @@ #ifndef UI_PLATFORM_WINDOW_X11_X11_WINDOW_H_ #define UI_PLATFORM_WINDOW_X11_X11_WINDOW_H_ -#include <stdint.h> - #include "base/macros.h" #include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/x/x11_atom_cache.h" -#include "ui/platform_window/platform_window.h" -#include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/x11/x11_window_base.h" #include "ui/platform_window/x11/x11_window_export.h" -typedef struct _XDisplay XDisplay; -typedef unsigned long XID; - namespace ui { -class X11_WINDOW_EXPORT X11Window : public PlatformWindow, +// PlatformWindow implementation for X11. PlatformEvents are XEvents. +class X11_WINDOW_EXPORT X11Window : public X11WindowBase, public PlatformEventDispatcher { public: explicit X11Window(PlatformWindowDelegate* delegate); ~X11Window() override; - private: - void Destroy(); - - void ProcessXInput2Event(XEvent* xevent); - // PlatformWindow: - void Show() override; - void Hide() override; - void Close() override; - void SetBounds(const gfx::Rect& bounds) override; - gfx::Rect GetBounds() override; - void SetTitle(const base::string16& title) override; - void SetCapture() override; - void ReleaseCapture() override; - void ToggleFullscreen() override; - void Maximize() override; - void Minimize() override; - void Restore() override; void SetCursor(PlatformCursor cursor) override; - void MoveCursorTo(const gfx::Point& location) override; - void ConfineCursorToBounds(const gfx::Rect& bounds) override; - PlatformImeController* GetPlatformImeController() override; + + private: + void ProcessXInput2Event(XEvent* xev); // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; - PlatformWindowDelegate* delegate_; - - XDisplay* xdisplay_; - XID xwindow_; - XID xroot_window_; - X11AtomCache atom_cache_; - - base::string16 window_title_; - - // Setting the bounds is an asynchronous operation in X11. |requested_bounds_| - // is the bounds requested using XConfigureWindow, and |confirmed_bounds_| is - // the bounds the X11 server has set on the window. - gfx::Rect requested_bounds_; - gfx::Rect confirmed_bounds_; - - bool window_mapped_; - DISALLOW_COPY_AND_ASSIGN(X11Window); }; -namespace test { - -// Sets the value of the |override_redirect| flag when creating an X11 window. -// It is necessary to set this flag on for various tests, otherwise the call to -// X11Window::Show() blocks because it never receives the MapNotify event. It is -// unclear why this is necessary, but might be related to calls to -// XInitThreads(). -X11_WINDOW_EXPORT void SetUseOverrideRedirectWindowByDefault( - bool override_redirect); - -} // namespace test - } // namespace ui #endif // UI_PLATFORM_WINDOW_X11_X11_WINDOW_H_ diff --git a/chromium/ui/platform_window/x11/x11_window_base.cc b/chromium/ui/platform_window/x11/x11_window_base.cc new file mode 100644 index 00000000000..51b19acfd25 --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_window_base.cc @@ -0,0 +1,288 @@ +// 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/platform_window/x11/x11_window_base.h" + +#include <X11/extensions/XInput2.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <string> + +#include "base/strings/utf_string_conversions.h" +#include "ui/events/devices/x11/touch_factory_x11.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/gfx/x/x11_types.h" +#include "ui/platform_window/platform_window_delegate.h" + +namespace ui { + +namespace { + +const char* kAtomsToCache[] = {"UTF8_STRING", "WM_DELETE_WINDOW", + "_NET_WM_NAME", "_NET_WM_PID", + "_NET_WM_PING", NULL}; + +bool g_override_redirect = false; + +XID FindXEventTarget(const XEvent& xev) { + XID target = xev.xany.window; + if (xev.type == GenericEvent) + target = static_cast<XIDeviceEvent*>(xev.xcookie.data)->event; + return target; +} + +} // namespace + +X11WindowBase::X11WindowBase(PlatformWindowDelegate* delegate) + : delegate_(delegate), + xdisplay_(gfx::GetXDisplay()), + xwindow_(None), + xroot_window_(DefaultRootWindow(xdisplay_)), + atom_cache_(xdisplay_, kAtomsToCache) { + DCHECK(delegate_); + TouchFactory::SetTouchDeviceListFromCommandLine(); +} + +X11WindowBase::~X11WindowBase() { + Destroy(); +} + +void X11WindowBase::Destroy() { + if (xwindow_ == None) + return; + + // Stop processing events. + XID xwindow = xwindow_; + XDisplay* xdisplay = xdisplay_; + xwindow_ = None; + delegate_->OnClosed(); + // |this| might be deleted because of the above call. + + XDestroyWindow(xdisplay, xwindow); +} + +void X11WindowBase::Create() { + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.background_pixmap = None; + swa.bit_gravity = NorthWestGravity; + swa.override_redirect = g_override_redirect; + xwindow_ = XCreateWindow( + xdisplay_, xroot_window_, requested_bounds_.x(), requested_bounds_.y(), + requested_bounds_.width(), requested_bounds_.height(), + 0, // border width + CopyFromParent, // depth + InputOutput, + CopyFromParent, // visual + CWBackPixmap | CWBitGravity | CWOverrideRedirect, &swa); + + long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | + KeyPressMask | KeyReleaseMask | EnterWindowMask | + LeaveWindowMask | ExposureMask | VisibilityChangeMask | + StructureNotifyMask | PropertyChangeMask | + PointerMotionMask; + XSelectInput(xdisplay_, xwindow_, event_mask); + + unsigned char mask[XIMaskLen(XI_LASTEVENT)]; + memset(mask, 0, sizeof(mask)); + + XISetMask(mask, XI_TouchBegin); + XISetMask(mask, XI_TouchUpdate); + XISetMask(mask, XI_TouchEnd); + XISetMask(mask, XI_ButtonPress); + XISetMask(mask, XI_ButtonRelease); + XISetMask(mask, XI_Motion); + XISetMask(mask, XI_KeyPress); + XISetMask(mask, XI_KeyRelease); + + XIEventMask evmask; + evmask.deviceid = XIAllDevices; + evmask.mask_len = sizeof(mask); + evmask.mask = mask; + XISelectEvents(xdisplay_, xwindow_, &evmask, 1); + XFlush(xdisplay_); + + ::Atom protocols[2]; + protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); + protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); + XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); + + // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with + // the desktop environment. + XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); + + // Likewise, the X server needs to know this window's pid so it knows which + // program to kill if the window hangs. + // XChangeProperty() expects "pid" to be long. + static_assert(sizeof(long) >= sizeof(pid_t), + "pid_t should not be larger than long"); + long pid = getpid(); + XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_PID"), + XA_CARDINAL, 32, PropModeReplace, + reinterpret_cast<unsigned char*>(&pid), 1); + // Before we map the window, set size hints. Otherwise, some window managers + // will ignore toplevel XMoveWindow commands. + XSizeHints size_hints; + size_hints.flags = PPosition | PWinGravity; + size_hints.x = requested_bounds_.x(); + size_hints.y = requested_bounds_.y(); + // Set StaticGravity so that the window position is not affected by the + // frame width when running with window manager. + size_hints.win_gravity = StaticGravity; + XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); + + // TODO(sky): provide real scale factor. + delegate_->OnAcceleratedWidgetAvailable(xwindow_, 1.f); +} + +void X11WindowBase::Show() { + if (window_mapped_) + return; + if (xwindow_ == None) + Create(); + + XMapWindow(xdisplay_, xwindow_); + + // We now block until our window is mapped. Some X11 APIs will crash and + // burn if passed |xwindow_| before the window is mapped, and XMapWindow is + // asynchronous. + if (X11EventSource::GetInstance()) + X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); + window_mapped_ = true; +} + +void X11WindowBase::Hide() { + if (!window_mapped_) + return; + XWithdrawWindow(xdisplay_, xwindow_, 0); + window_mapped_ = false; +} + +void X11WindowBase::Close() { + Destroy(); +} + +void X11WindowBase::SetBounds(const gfx::Rect& bounds) { + requested_bounds_ = bounds; + if (!window_mapped_ || bounds == confirmed_bounds_) + return; + XWindowChanges changes = {0}; + unsigned value_mask = CWX | CWY | CWWidth | CWHeight; + changes.x = bounds.x(); + changes.y = bounds.y(); + changes.width = bounds.width(); + changes.height = bounds.height(); + XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); +} + +gfx::Rect X11WindowBase::GetBounds() { + return confirmed_bounds_; +} + +void X11WindowBase::SetTitle(const base::string16& title) { + if (window_title_ == title) + return; + window_title_ = title; + std::string utf8str = base::UTF16ToUTF8(title); + XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_NAME"), + atom_cache_.GetAtom("UTF8_STRING"), 8, PropModeReplace, + reinterpret_cast<const unsigned char*>(utf8str.c_str()), + utf8str.size()); + XTextProperty xtp; + char* c_utf8_str = const_cast<char*>(utf8str.c_str()); + if (Xutf8TextListToTextProperty(xdisplay_, &c_utf8_str, 1, XUTF8StringStyle, + &xtp) == Success) { + XSetWMName(xdisplay_, xwindow_, &xtp); + XFree(xtp.value); + } +} + +void X11WindowBase::SetCapture() {} + +void X11WindowBase::ReleaseCapture() {} + +void X11WindowBase::ToggleFullscreen() {} + +void X11WindowBase::Maximize() {} + +void X11WindowBase::Minimize() {} + +void X11WindowBase::Restore() {} + +void X11WindowBase::MoveCursorTo(const gfx::Point& location) { + XWarpPointer(xdisplay_, None, xroot_window_, 0, 0, 0, 0, + confirmed_bounds_.x() + location.x(), + confirmed_bounds_.y() + location.y()); +} + +void X11WindowBase::ConfineCursorToBounds(const gfx::Rect& bounds) {} + +PlatformImeController* X11WindowBase::GetPlatformImeController() { + return nullptr; +} + +bool X11WindowBase::IsEventForXWindow(const XEvent& xev) const { + return xwindow_ != None && FindXEventTarget(xev) == xwindow_; +} + +void X11WindowBase::ProcessXWindowEvent(XEvent* xev) { + switch (xev->type) { + case Expose: { + gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, xev->xexpose.width, + xev->xexpose.height); + delegate_->OnDamageRect(damage_rect); + break; + } + + case FocusOut: + if (xev->xfocus.mode != NotifyGrab) + delegate_->OnLostCapture(); + break; + + case ConfigureNotify: { + DCHECK_EQ(xwindow_, xev->xconfigure.event); + DCHECK_EQ(xwindow_, xev->xconfigure.window); + gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, + xev->xconfigure.width, xev->xconfigure.height); + if (confirmed_bounds_ != bounds) { + confirmed_bounds_ = bounds; + delegate_->OnBoundsChanged(confirmed_bounds_); + } + break; + } + + case ClientMessage: { + Atom message = static_cast<Atom>(xev->xclient.data.l[0]); + if (message == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { + delegate_->OnCloseRequest(); + } else if (message == atom_cache_.GetAtom("_NET_WM_PING")) { + XEvent reply_event = *xev; + reply_event.xclient.window = xroot_window_; + + XSendEvent(xdisplay_, reply_event.xclient.window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &reply_event); + XFlush(xdisplay_); + } + break; + } + } +} + +namespace test { + +void SetUseOverrideRedirectWindowByDefault(bool override_redirect) { + g_override_redirect = override_redirect; +} + +} // namespace test +} // namespace ui diff --git a/chromium/ui/platform_window/x11/x11_window_base.h b/chromium/ui/platform_window/x11/x11_window_base.h new file mode 100644 index 00000000000..da357253612 --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_window_base.h @@ -0,0 +1,100 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_PLATFORM_WINDOW_X11_X11_WINDOW_BASE_H_ +#define UI_PLATFORM_WINDOW_X11_X11_WINDOW_BASE_H_ + +#include <stdint.h> + +#include "base/callback.h" +#include "base/macros.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/platform_window/platform_window.h" +#include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/x11/x11_window_export.h" + +typedef struct _XDisplay XDisplay; +typedef unsigned long XID; +typedef union _XEvent XEvent; + +namespace ui { + +// Abstract base implementation for a X11 based PlatformWindow. Methods that +// are platform specific are left unimplemented. +class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow { + public: + explicit X11WindowBase(PlatformWindowDelegate* delegate); + ~X11WindowBase() override; + + // Creates new underlying XWindow. Does not map XWindow. + void Create(); + + // PlatformWindow: + void Show() override; + void Hide() override; + void Close() override; + void SetBounds(const gfx::Rect& bounds) override; + gfx::Rect GetBounds() override; + void SetTitle(const base::string16& title) override; + void SetCapture() override; + void ReleaseCapture() override; + void ToggleFullscreen() override; + void Maximize() override; + void Minimize() override; + void Restore() override; + void MoveCursorTo(const gfx::Point& location) override; + void ConfineCursorToBounds(const gfx::Rect& bounds) override; + PlatformImeController* GetPlatformImeController() override; + + protected: + void Destroy(); + + PlatformWindowDelegate* delegate() { return delegate_; } + XDisplay* xdisplay() { return xdisplay_; } + XID xwindow() const { return xwindow_; } + + // Checks if XEvent is for this XWindow. + bool IsEventForXWindow(const XEvent& xev) const; + + // Processes events for this XWindow. + void ProcessXWindowEvent(XEvent* xev); + + private: + PlatformWindowDelegate* delegate_; + + XDisplay* xdisplay_; + XID xwindow_; + XID xroot_window_; + X11AtomCache atom_cache_; + + base::string16 window_title_; + + // Setting the bounds is an asynchronous operation in X11. |requested_bounds_| + // is the bounds requested using XConfigureWindow, and |confirmed_bounds_| is + // the bounds the X11 server has set on the window. + gfx::Rect requested_bounds_; + gfx::Rect confirmed_bounds_; + + bool window_mapped_ = false; + + DISALLOW_COPY_AND_ASSIGN(X11WindowBase); +}; + +namespace test { + +// Sets the value of the |override_redirect| flag when creating an X11 window. +// It is necessary to set this flag on for various tests, otherwise the call to +// X11WindowBase::Show() blocks because it never receives the MapNotify event. +// It is +// unclear why this is necessary, but might be related to calls to +// XInitThreads(). +X11_WINDOW_EXPORT void SetUseOverrideRedirectWindowByDefault( + bool override_redirect); + +} // namespace test + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_X11_X11_WINDOW_BASE_H_ diff --git a/chromium/ui/platform_window/x11/x11_window_manager_ozone.cc b/chromium/ui/platform_window/x11/x11_window_manager_ozone.cc new file mode 100644 index 00000000000..f29cf9e9433 --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_window_manager_ozone.cc @@ -0,0 +1,27 @@ +// 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/platform_window/x11/x11_window_manager_ozone.h" + +#include <X11/Xlib.h> + +namespace ui { + +X11WindowManagerOzone::X11WindowManagerOzone() : event_grabber_(None) {} + +X11WindowManagerOzone::~X11WindowManagerOzone() {} + +void X11WindowManagerOzone::GrabEvents(XID xwindow) { + if (event_grabber_ != None) + return; + event_grabber_ = xwindow; +} + +void X11WindowManagerOzone::UngrabEvents(XID xwindow) { + if (event_grabber_ != xwindow) + return; + event_grabber_ = None; +} + +} // namespace ui diff --git a/chromium/ui/platform_window/x11/x11_window_manager_ozone.h b/chromium/ui/platform_window/x11/x11_window_manager_ozone.h new file mode 100644 index 00000000000..1cb6bd1bacd --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_window_manager_ozone.h @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_PLATFORM_WINDOW_X11_X11_WINDOW_MANAGER_OZONE_H_ +#define UI_PLATFORM_WINDOW_X11_X11_WINDOW_MANAGER_OZONE_H_ + +#include "base/macros.h" +#include "ui/platform_window/x11/x11_window_export.h" +#include "ui/platform_window/x11/x11_window_ozone.h" + +namespace ui { + +class X11_WINDOW_EXPORT X11WindowManagerOzone { + public: + X11WindowManagerOzone(); + ~X11WindowManagerOzone(); + + // Tries to set a given XWindow as the recipient for events. It will fail if + // there is already another XWindow as recipient. + void GrabEvents(XID xwindow); + + // Unsets a given XWindow as the recipient for events. + void UngrabEvents(XID xwindow); + + // Gets the current XWindow recipient of mouse events. + XID event_grabber() const { return event_grabber_; } + + private: + XID event_grabber_; + + DISALLOW_COPY_AND_ASSIGN(X11WindowManagerOzone); +}; + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_X11_X11_WINDOW_MANAGER_OZONE_H_ diff --git a/chromium/ui/platform_window/x11/x11_window_ozone.cc b/chromium/ui/platform_window/x11/x11_window_ozone.cc new file mode 100644 index 00000000000..5a8307004f7 --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_window_ozone.cc @@ -0,0 +1,85 @@ +// 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/platform_window/x11/x11_window_ozone.h" + +#include <X11/Xlib.h> + +#include "ui/events/event.h" +#include "ui/events/ozone/events_ozone.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/geometry/point.h" +#include "ui/platform_window/x11/x11_cursor_ozone.h" +#include "ui/platform_window/x11/x11_window_manager_ozone.h" + +namespace ui { + +X11WindowOzone::X11WindowOzone(X11EventSourceLibevent* event_source, + X11WindowManagerOzone* window_manager, + PlatformWindowDelegate* delegate) + : X11WindowBase(delegate), + event_source_(event_source), + window_manager_(window_manager) { + DCHECK(event_source_); + DCHECK(window_manager); + event_source_->AddPlatformEventDispatcher(this); + event_source_->AddXEventDispatcher(this); +} + +X11WindowOzone::~X11WindowOzone() { + event_source_->RemovePlatformEventDispatcher(this); + event_source_->RemoveXEventDispatcher(this); +} + +void X11WindowOzone::SetCapture() { + window_manager_->GrabEvents(xwindow()); +} + +void X11WindowOzone::ReleaseCapture() { + window_manager_->UngrabEvents(xwindow()); +} + +void X11WindowOzone::SetCursor(PlatformCursor cursor) { + X11CursorOzone* cursor_ozone = static_cast<X11CursorOzone*>(cursor); + XDefineCursor(xdisplay(), xwindow(), cursor_ozone->xcursor()); +} + +bool X11WindowOzone::DispatchXEvent(XEvent* xev) { + if (!IsEventForXWindow(*xev)) + return false; + + ProcessXWindowEvent(xev); + return true; +} + +bool X11WindowOzone::CanDispatchEvent(const PlatformEvent& platform_event) { + if (xwindow() == None) + return false; + + // If there is a grab, capture events here. + XID grabber = window_manager_->event_grabber(); + if (grabber != None) + return grabber == xwindow(); + + // TODO(kylechar): We may need to do something special for TouchEvents similar + // to how DrmWindowHost handles them. + if (static_cast<Event*>(platform_event)->IsLocatedEvent()) { + const LocatedEvent* event = + static_cast<const LocatedEvent*>(platform_event); + return GetBounds().Contains(event->root_location()); + } + + return true; +} + +uint32_t X11WindowOzone::DispatchEvent(const PlatformEvent& platform_event) { + // This is unfortunately needed otherwise events that depend on global state + // (eg. double click) are broken. + DispatchEventFromNativeUiEvent( + platform_event, base::Bind(&PlatformWindowDelegate::DispatchEvent, + base::Unretained(delegate()))); + return POST_DISPATCH_STOP_PROPAGATION; +} + +} // namespace ui diff --git a/chromium/ui/platform_window/x11/x11_window_ozone.h b/chromium/ui/platform_window/x11/x11_window_ozone.h new file mode 100644 index 00000000000..da847ecc8a1 --- /dev/null +++ b/chromium/ui/platform_window/x11/x11_window_ozone.h @@ -0,0 +1,49 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_PLATFORM_WINDOW_X11_X11_WINDOW_OZONE_H_ +#define UI_PLATFORM_WINDOW_X11_X11_WINDOW_OZONE_H_ + +#include "base/macros.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/platform/x11/x11_event_source_libevent.h" +#include "ui/platform_window/x11/x11_window_base.h" +#include "ui/platform_window/x11/x11_window_export.h" + +namespace ui { + +class X11WindowManagerOzone; + +// PlatformWindow implementation for X11 Ozone. PlatformEvents are ui::Events. +class X11_WINDOW_EXPORT X11WindowOzone : public X11WindowBase, + public PlatformEventDispatcher, + public XEventDispatcher { + public: + X11WindowOzone(X11EventSourceLibevent* event_source, + X11WindowManagerOzone* window_manager, + PlatformWindowDelegate* delegate); + ~X11WindowOzone() override; + + // PlatformWindow: + void SetCapture() override; + void ReleaseCapture() override; + void SetCursor(PlatformCursor cursor) override; + + // XEventDispatcher: + bool DispatchXEvent(XEvent* event) override; + + private: + // PlatformEventDispatcher: + bool CanDispatchEvent(const PlatformEvent& event) override; + uint32_t DispatchEvent(const PlatformEvent& event) override; + + X11EventSourceLibevent* event_source_; + X11WindowManagerOzone* window_manager_; + + DISALLOW_COPY_AND_ASSIGN(X11WindowOzone); +}; + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_X11_X11_WINDOW_OZONE_H_ diff --git a/chromium/ui/resources/BUILD.gn b/chromium/ui/resources/BUILD.gn index eaf4646b4ea..1b407f3712f 100644 --- a/chromium/ui/resources/BUILD.gn +++ b/chromium/ui/resources/BUILD.gn @@ -71,6 +71,60 @@ if (is_ios || is_mac) { ":repack_ui_test_pak", ] } + + # This needs to be a separate target from ui_test_pak as ui_base_unittests + # wants the generate .pak file in the bundle to be named en.lpro/locale.pak + # while components_unittests wants it to be named ui_test.pak. + bundle_data("ui_test_pak_bundle_data_locale") { + testonly = true + visibility = [ ":ui_test_pak_bundle_data" ] + public_deps = [ + ":ui_test_pak", + ] + sources = [ + "$root_out_dir/ui_test.pak", + ] + outputs = [ + "{{bundle_resources_dir}}/en.lproj/locale.pak", + ] + } + + bundle_data("ui_test_pak_bundle_data_test_pak") { + testonly = true + visibility = [ ":ui_test_pak_bundle_data" ] + public_deps = [ + ":ui_test_pak", + ] + sources = [ + "$root_out_dir/ui_test.pak", + ] + outputs = [ + "{{bundle_resources_dir}}/ui_test.pak", + ] + } + + bundle_data("ui_test_pak_bundle_data_100_percent") { + testonly = true + visibility = [ ":ui_test_pak_bundle_data" ] + public_deps = [ + ":ui_test_pak", + ] + sources = [ + "$root_out_dir/ui_test.pak", + ] + outputs = [ + "{{bundle_resources_dir}}/chrome_100_percent.pak", + ] + } + + group("ui_test_pak_bundle_data") { + testonly = true + public_deps = [ + ":ui_test_pak_bundle_data_100_percent", + ":ui_test_pak_bundle_data_locale", + ":ui_test_pak_bundle_data_test_pak", + ] + } } else { copy("ui_test_pak") { sources = [ diff --git a/chromium/ui/shell_dialogs/BUILD.gn b/chromium/ui/shell_dialogs/BUILD.gn index 4ecd64c4f47..baadbba665b 100644 --- a/chromium/ui/shell_dialogs/BUILD.gn +++ b/chromium/ui/shell_dialogs/BUILD.gn @@ -14,8 +14,6 @@ component("shell_dialogs") { "base_shell_dialog.h", "base_shell_dialog_win.cc", "base_shell_dialog_win.h", - "linux_shell_dialog.cc", - "linux_shell_dialog.h", "select_file_dialog.cc", "select_file_dialog.h", "select_file_dialog_factory.cc", @@ -28,6 +26,8 @@ component("shell_dialogs") { "select_file_policy.h", "selected_file_info.cc", "selected_file_info.h", + "shell_dialog_linux.cc", + "shell_dialog_linux.h", ] defines = [ "SHELL_DIALOGS_IMPLEMENTATION" ] @@ -63,29 +63,21 @@ component("shell_dialogs") { include_dirs = [ "$root_gen_dir/ui" ] libs = [ "jnigraphics" ] } - - if (is_android && use_aura) { - sources += [ - "select_file_dialog_auraandroid.cc", - "select_file_dialog_auraandroid.h", - ] - } - - if (is_win) { - deps += [ "//win8:metro_viewer" ] - } } test("shell_dialogs_unittests") { sources = [ + # TODO(karandeepb) : Revisit this once gn gets mac bundle support. + # "select_file_dialog_mac_unittest.mm", + "run_all_unittests.cc", "select_file_dialog_win_unittest.cc", ] deps = [ ":shell_dialogs", "//base", - "//base/test:run_all_unittests", "//base/test:test_support", "//testing/gtest", + "//ui/base:base", ] } diff --git a/chromium/ui/shell_dialogs/linux_shell_dialog.cc b/chromium/ui/shell_dialogs/linux_shell_dialog.cc deleted file mode 100644 index 059bb724564..00000000000 --- a/chromium/ui/shell_dialogs/linux_shell_dialog.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/shell_dialogs/linux_shell_dialog.h" - -namespace { - -ui::LinuxShellDialog* g_linux_shell_dialog = NULL; - -} // namespace - -namespace ui { - -void LinuxShellDialog::SetInstance(LinuxShellDialog* instance) { - g_linux_shell_dialog = instance; -} - -const LinuxShellDialog* LinuxShellDialog::instance() { - return g_linux_shell_dialog; -} - -} // namespace ui diff --git a/chromium/ui/shell_dialogs/run_all_unittests.cc b/chromium/ui/shell_dialogs/run_all_unittests.cc new file mode 100644 index 00000000000..08996518bc9 --- /dev/null +++ b/chromium/ui/shell_dialogs/run_all_unittests.cc @@ -0,0 +1,73 @@ +// 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 "base/bind.h" +#include "base/macros.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include "base/files/file_path.h" +#include "base/mac/bundle_locations.h" +#include "base/path_service.h" +#include "base/test/mock_chrome_application_mac.h" +#include "ui/base/resource/resource_bundle.h" +#endif + +namespace { + +class ShellDialogsTestSuite : public base::TestSuite { + public: + ShellDialogsTestSuite(int argc, char** argv); + + protected: + // base::TestSuite: + void Initialize() override; + void Shutdown() override; + + private: + DISALLOW_COPY_AND_ASSIGN(ShellDialogsTestSuite); +}; + +ShellDialogsTestSuite::ShellDialogsTestSuite(int argc, char** argv) + : base::TestSuite(argc, argv) {} + +void ShellDialogsTestSuite::Initialize() { + base::TestSuite::Initialize(); + +#if defined(OS_MACOSX) + mock_cr_app::RegisterMockCrApp(); + + // Set up framework bundle so that tests on Mac can access nib files. + base::FilePath path; + PathService::Get(base::DIR_EXE, &path); + // The three DirName() calls strip "Contents/MacOS/<binary>" from the path. + path = path.DirName().DirName().DirName(); + path = path.Append(FILE_PATH_LITERAL("shell_dialogs_unittests.app")); + base::mac::SetOverrideFrameworkBundlePath(path); + + // Setup resource bundle. + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES); +#endif +} + +void ShellDialogsTestSuite::Shutdown() { +#if defined(OS_MACOSX) + ui::ResourceBundle::CleanupSharedInstance(); + base::mac::SetOverrideFrameworkBundle(NULL); +#endif + base::TestSuite::Shutdown(); +} + +} // namespace + +int main(int argc, char** argv) { + ShellDialogsTestSuite test_suite(argc, argv); + + return base::LaunchUnitTests( + argc, argv, + base::Bind(&ShellDialogsTestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/chromium/ui/shell_dialogs/select_file_dialog.cc b/chromium/ui/shell_dialogs/select_file_dialog.cc index 6249ba8fada..fd2a7cf6ce5 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog.cc @@ -13,33 +13,21 @@ #include "ui/shell_dialogs/select_file_dialog_factory.h" #include "ui/shell_dialogs/select_file_policy.h" #include "ui/shell_dialogs/selected_file_info.h" -#include "ui/shell_dialogs/shell_dialogs_delegate.h" - -#if defined(OS_WIN) -#include "ui/shell_dialogs/select_file_dialog_win.h" -#elif defined(OS_MACOSX) -#include "ui/shell_dialogs/select_file_dialog_mac.h" -#elif defined(OS_ANDROID) -#include "ui/shell_dialogs/select_file_dialog_android.h" -#elif defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS) -#include "ui/shell_dialogs/linux_shell_dialog.h" -#endif namespace { // Optional dialog factory. Leaked. ui::SelectFileDialogFactory* dialog_factory_ = NULL; -// The global shell dialogs delegate. -ui::ShellDialogsDelegate* g_shell_dialogs_delegate_ = NULL; - } // namespace namespace ui { SelectFileDialog::FileTypeInfo::FileTypeInfo() - : include_all_files(false), - support_drive(false) {} + : include_all_files(false), allowed_paths(NATIVE_PATH) {} + +SelectFileDialog::FileTypeInfo::FileTypeInfo(const FileTypeInfo& other) = + default; SelectFileDialog::FileTypeInfo::~FileTypeInfo() {} @@ -81,24 +69,7 @@ scoped_refptr<SelectFileDialog> SelectFileDialog::Create( return dialog; } -#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS) - const ui::LinuxShellDialog* shell_dialogs = ui::LinuxShellDialog::instance(); - if (shell_dialogs) - return shell_dialogs->CreateSelectFileDialog(listener, policy); -#endif - -#if defined(OS_WIN) - // TODO(ananta) - // Fix this for Chrome ASH on Windows. - return CreateDefaultWinSelectFileDialog(listener, policy); -#elif defined(OS_MACOSX) && !defined(USE_AURA) - return CreateMacSelectFileDialog(listener, policy); -#elif defined(OS_ANDROID) - return CreateAndroidSelectFileDialog(listener, policy); -#else - NOTIMPLEMENTED(); - return NULL; -#endif + return CreateSelectFileDialog(listener, policy); } void SelectFileDialog::SelectFile( @@ -134,11 +105,6 @@ bool SelectFileDialog::HasMultipleFileTypeChoices() { return HasMultipleFileTypeChoicesImpl(); } -// static -void SelectFileDialog::SetShellDialogsDelegate(ShellDialogsDelegate* delegate) { - g_shell_dialogs_delegate_ = delegate; -} - SelectFileDialog::SelectFileDialog(Listener* listener, ui::SelectFilePolicy* policy) : listener_(listener), @@ -153,8 +119,4 @@ void SelectFileDialog::CancelFileSelection(void* params) { listener_->FileSelectionCanceled(params); } -ShellDialogsDelegate* SelectFileDialog::GetShellDialogsDelegate() { - return g_shell_dialogs_delegate_; -} - } // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog.h b/chromium/ui/shell_dialogs/select_file_dialog.h index bd610f59654..e89f686ceca 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog.h +++ b/chromium/ui/shell_dialogs/select_file_dialog.h @@ -109,6 +109,7 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog // Holds information about allowed extensions on a file save dialog. struct SHELL_DIALOGS_EXPORT FileTypeInfo { FileTypeInfo(); + FileTypeInfo(const FileTypeInfo& other); ~FileTypeInfo(); // A list of allowed extensions. For example, it might be @@ -127,12 +128,12 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog // Specifies whether there will be a filter added for all files (i.e. *.*). bool include_all_files; - // Specifies whether the caller can directly support file paths pointing to - // files/folders on Google Drive. If the flag is true, the file dialog does - // nothing special; just returns a Drive path. If it is false, the dialog - // creates a local replica of the Drive file and returns its path, so that - // the caller can use it without any difference than when it were local. - bool support_drive; + // Specifies which type of paths the caller can handle. If it is + // NATIVE_PATH, the dialog creates a native replica of the non-native file + // and returns its path, so that the caller can use it without any + // difference than when it were local. + enum AllowedPaths { ANY_PATH, NATIVE_PATH, NATIVE_OR_DRIVE_PATH }; + AllowedPaths allowed_paths; }; // Selects a File. @@ -172,9 +173,6 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog void* params); bool HasMultipleFileTypeChoices(); - // Sets the global ShellDialogsDelegate. Defaults to NULL. - static void SetShellDialogsDelegate(ShellDialogsDelegate* delegate); - protected: friend class base::RefCountedThreadSafe<SelectFileDialog>; explicit SelectFileDialog(Listener* listener, SelectFilePolicy* policy); @@ -194,9 +192,6 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog gfx::NativeWindow owning_window, void* params) = 0; - // Returns the global ShellDialogsDelegate instance if any. - ShellDialogsDelegate* GetShellDialogsDelegate(); - // The listener to be notified of selection completion. Listener* listener_; @@ -218,6 +213,8 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog DISALLOW_COPY_AND_ASSIGN(SelectFileDialog); }; +SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, + 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 b0c75320a91..11b9d4c824b 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_android.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog_android.cc @@ -145,9 +145,8 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { return false; } -SelectFileDialog* CreateAndroidSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { +SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, + SelectFilePolicy* policy) { return SelectFileDialogImpl::Create(listener, policy); } diff --git a/chromium/ui/shell_dialogs/select_file_dialog_android.h b/chromium/ui/shell_dialogs/select_file_dialog_android.h index 0dba18e483a..5291cd3c92b 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_android.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_android.h @@ -65,10 +65,6 @@ class SelectFileDialogImpl : public SelectFileDialog { DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); }; -SelectFileDialog* CreateAndroidSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy); - } // namespace ui #endif // UI_SHELL_DIALOGS_ANDROID_SELECT_FILE_DIALOG_ANDROID_H_ diff --git a/chromium/ui/shell_dialogs/select_file_dialog_auraandroid.cc b/chromium/ui/shell_dialogs/select_file_dialog_auraandroid.cc deleted file mode 100644 index 185f47ed2ca..00000000000 --- a/chromium/ui/shell_dialogs/select_file_dialog_auraandroid.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "select_file_dialog_auraandroid.h" - -#include "base/logging.h" - -namespace ui { - -// static -SelectFileDialogImpl* SelectFileDialogImpl::Create(Listener* listener, - SelectFilePolicy* policy) { - return new SelectFileDialogImpl(listener, policy); -} - -bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow) const { - return listener_; -} - -void SelectFileDialogImpl::ListenerDestroyed() { - listener_ = NULL; -} - -void SelectFileDialogImpl::SelectFileImpl( - SelectFileDialog::Type type, - const base::string16& title, - const base::FilePath& default_path, - const SelectFileDialog::FileTypeInfo* file_types, - int file_type_index, - const std::string& default_extension, - gfx::NativeWindow owning_window, - void* params) { - NOTIMPLEMENTED(); -} - -SelectFileDialogImpl::~SelectFileDialogImpl() { -} - -SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener, - SelectFilePolicy* policy) - : SelectFileDialog(listener, policy) { -} - -bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { - NOTIMPLEMENTED(); - return false; -} - -SelectFileDialog* CreateAndroidSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { - return SelectFileDialogImpl::Create(listener, policy); -} - -} // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog_auraandroid.h b/chromium/ui/shell_dialogs/select_file_dialog_auraandroid.h deleted file mode 100644 index 5b7a1630974..00000000000 --- a/chromium/ui/shell_dialogs/select_file_dialog_auraandroid.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_SHELL_DIALOGS_ANDROID_SELECT_FILE_DIALOG_ANDROID_H_ -#define UI_SHELL_DIALOGS_ANDROID_SELECT_FILE_DIALOG_ANDROID_H_ - -#include "base/macros.h" -#include "ui/shell_dialogs/select_file_dialog.h" - -namespace ui { - -class SelectFileDialogImpl : public SelectFileDialog { - public: - static SelectFileDialogImpl* Create(Listener* listener, - SelectFilePolicy* policy); - - // From SelectFileDialog - bool IsRunning(gfx::NativeWindow) const override; - void ListenerDestroyed() override; - void SelectFileImpl(SelectFileDialog::Type type, - const base::string16& title, - const base::FilePath& default_path, - const SelectFileDialog::FileTypeInfo* file_types, - int file_type_index, - const std::string& default_extension, - gfx::NativeWindow owning_window, - void* params) override; - - protected: - ~SelectFileDialogImpl() override; - - private: - SelectFileDialogImpl(Listener* listener, SelectFilePolicy* policy); - - bool HasMultipleFileTypeChoicesImpl() override; - - DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); -}; - -SelectFileDialog* CreateAndroidSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy); - -} // namespace ui - -#endif // UI_SHELL_DIALOGS_ANDROID_SELECT_FILE_DIALOG_ANDROID_H_ - diff --git a/chromium/ui/shell_dialogs/select_file_dialog_mac.h b/chromium/ui/shell_dialogs/select_file_dialog_mac.h index 8915a0854f1..b3765ef4224 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_mac.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_mac.h @@ -5,14 +5,100 @@ #ifndef UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_MAC_H_ #define UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_MAC_H_ +#import <Cocoa/Cocoa.h> + +#include <map> +#include <set> + +#import "base/mac/scoped_nsobject.h" +#include "base/macros.h" #include "ui/gfx/native_widget_types.h" #include "ui/shell_dialogs/select_file_dialog.h" +#include "ui/shell_dialogs/shell_dialogs_export.h" + +@class ExtensionDropdownHandler; +@class SelectFileDialogBridge; namespace ui { -SelectFileDialog* CreateMacSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy); +namespace test { +class SelectFileDialogMacTest; +} // namespace test + +// Implementation of SelectFileDialog that shows Cocoa dialogs for choosing a +// file or folder. +// Exported for unit tests. +class SHELL_DIALOGS_EXPORT SelectFileDialogImpl : public ui::SelectFileDialog { + public: + SelectFileDialogImpl(Listener* listener, ui::SelectFilePolicy* policy); + + // BaseShellDialog implementation. + bool IsRunning(gfx::NativeWindow parent_window) const override; + void ListenerDestroyed() override; + + // Callback from ObjC bridge. + void FileWasSelected(NSSavePanel* dialog, + NSWindow* parent_window, + bool was_cancelled, + bool is_multi, + const std::vector<base::FilePath>& files, + int index); + + protected: + // SelectFileDialog implementation. + // |params| is user data we pass back via the Listener interface. + void SelectFileImpl(Type type, + const base::string16& title, + const base::FilePath& default_path, + const FileTypeInfo* file_types, + int file_type_index, + const base::FilePath::StringType& default_extension, + gfx::NativeWindow owning_window, + void* params) override; + + private: + friend class test::SelectFileDialogMacTest; + + // Struct to store data associated with a file dialog while it is showing. + struct DialogData { + DialogData(void* params_, + base::scoped_nsobject<ExtensionDropdownHandler> handler); + DialogData(const DialogData& other); + + // |params| user data associated with this file dialog. + void* params; + + // Extension dropdown handler corresponding to this file dialog. + base::scoped_nsobject<ExtensionDropdownHandler> extension_dropdown_handler; + + ~DialogData(); + }; + + ~SelectFileDialogImpl() override; + + // Sets the accessory view for the |dialog| and returns the associated + // ExtensionDropdownHandler. + static base::scoped_nsobject<ExtensionDropdownHandler> SetAccessoryView( + NSSavePanel* dialog, + const FileTypeInfo* file_types, + int file_type_index, + const base::FilePath::StringType& default_extension); + + bool HasMultipleFileTypeChoicesImpl() override; + + // The bridge for results from Cocoa to return to us. + base::scoped_nsobject<SelectFileDialogBridge> bridge_; + + // The set of all parent windows for which we are currently running dialogs. + std::set<NSWindow*> parents_; + + // A map from file dialogs to the DialogData associated with them. + std::map<NSSavePanel*, DialogData> dialog_data_map_; + + bool hasMultipleFileTypeChoices_; + + DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); +}; } // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog_mac.mm b/chromium/ui/shell_dialogs/select_file_dialog_mac.mm index 6fe6a62946a..1ac81223630 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_mac.mm +++ b/chromium/ui/shell_dialogs/select_file_dialog_mac.mm @@ -2,14 +2,11 @@ // 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 "ui/shell_dialogs/select_file_dialog_mac.h" -#import <Cocoa/Cocoa.h> #include <CoreServices/CoreServices.h> #include <stddef.h> -#include <map> -#include <set> #include <vector> #include "base/files/file_util.h" @@ -18,8 +15,6 @@ #include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" #include "base/mac/scoped_cftyperef.h" -#import "base/mac/scoped_nsobject.h" -#include "base/macros.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" @@ -55,16 +50,14 @@ NSString* GetDescriptionFromExtension(const base::FilePath::StringType& ext) { } // namespace -class SelectFileDialogImpl; - // A bridge class to act as the modal delegate to the save/open sheet and send // the results to the C++ class. @interface SelectFileDialogBridge : NSObject<NSOpenSavePanelDelegate> { @private - SelectFileDialogImpl* selectFileDialogImpl_; // WEAK; owns us + ui::SelectFileDialogImpl* selectFileDialogImpl_; // WEAK; owns us } -- (id)initWithSelectFileDialogImpl:(SelectFileDialogImpl*)s; +- (id)initWithSelectFileDialogImpl:(ui::SelectFileDialogImpl*)s; - (void)endedPanel:(NSSavePanel*)panel didCancel:(bool)did_cancel type:(ui::SelectFileDialog::Type)type @@ -92,76 +85,7 @@ class SelectFileDialogImpl; - (void)popupAction:(id)sender; @end -// Implementation of SelectFileDialog that shows Cocoa dialogs for choosing a -// file or folder. -class SelectFileDialogImpl : public ui::SelectFileDialog { - public: - explicit SelectFileDialogImpl(Listener* listener, - ui::SelectFilePolicy* policy); - - // BaseShellDialog implementation. - bool IsRunning(gfx::NativeWindow parent_window) const override; - void ListenerDestroyed() override; - - // Callback from ObjC bridge. - void FileWasSelected(NSSavePanel* dialog, - NSWindow* parent_window, - bool was_cancelled, - bool is_multi, - const std::vector<base::FilePath>& files, - int index); - - protected: - // SelectFileDialog implementation. - // |params| is user data we pass back via the Listener interface. - void SelectFileImpl(Type type, - const base::string16& title, - const base::FilePath& default_path, - const FileTypeInfo* file_types, - int file_type_index, - const base::FilePath::StringType& default_extension, - gfx::NativeWindow owning_window, - void* params) override; - - private: - // Struct to store data associated with a file dialog while it is showing. - struct DialogData { - DialogData(void* params_, - base::scoped_nsobject<ExtensionDropdownHandler> handler) - : params(params_), extension_dropdown_handler(handler) {} - - // |params| user data associated with this file dialog. - void* params; - - // Extension dropdown handler corresponding to this file dialog. - base::scoped_nsobject<ExtensionDropdownHandler> extension_dropdown_handler; - }; - - ~SelectFileDialogImpl() override; - - // Sets the accessory view for the |dialog| and returns the associated - // ExtensionDropdownHandler. - static base::scoped_nsobject<ExtensionDropdownHandler> SetAccessoryView( - NSSavePanel* dialog, - const FileTypeInfo* file_types, - int file_type_index, - const base::FilePath::StringType& default_extension); - - bool HasMultipleFileTypeChoicesImpl() override; - - // The bridge for results from Cocoa to return to us. - base::scoped_nsobject<SelectFileDialogBridge> bridge_; - - // The set of all parent windows for which we are currently running dialogs. - std::set<NSWindow*> parents_; - - // A map from file dialogs to the DialogData associated with them. - std::map<NSSavePanel*, DialogData> dialog_data_map_; - - bool hasMultipleFileTypeChoices_; - - DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); -}; +namespace ui { SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener, ui::SelectFilePolicy* policy) @@ -319,6 +243,15 @@ void SelectFileDialogImpl::SelectFileImpl( }]; } +SelectFileDialogImpl::DialogData::DialogData( + void* params_, + base::scoped_nsobject<ExtensionDropdownHandler> handler) + : params(params_), extension_dropdown_handler(handler) {} + +SelectFileDialogImpl::DialogData::DialogData(const DialogData& other) = default; + +SelectFileDialogImpl::DialogData::~DialogData() {} + SelectFileDialogImpl::~SelectFileDialogImpl() { // Walk through the open dialogs and close them all. Use a temporary vector // to hold the pointers, since we can't delete from the map as we're iterating @@ -350,6 +283,7 @@ SelectFileDialogImpl::SetAccessoryView( // Create an array with each item corresponding to an array of different // extensions in an extension group. NSMutableArray* file_type_lists = [NSMutableArray array]; + int default_extension_index = -1; for (size_t i = 0; i < file_types->extensions.size(); ++i) { const std::vector<base::FilePath::StringType>& ext_list = file_types->extensions[i]; @@ -373,6 +307,9 @@ SelectFileDialogImpl::SetAccessoryView( // Set to store different extensions in the current extension group. NSMutableSet* file_type_set = [NSMutableSet set]; for (const base::FilePath::StringType& ext : ext_list) { + if (ext == default_extension) + default_extension_index = i; + base::ScopedCFTypeRef<CFStringRef> uti(CreateUTIFromExtension(ext)); [file_type_set addObject:base::mac::CFToNSCast(uti.get())]; @@ -401,14 +338,21 @@ SelectFileDialogImpl::SetAccessoryView( [popup setTarget:handler]; [popup setAction:@selector(popupAction:)]; - if (default_extension.empty()) { - // Select the first item. - [popup selectItemAtIndex:0]; + // file_type_index uses 1 based indexing. + if (file_type_index) { + DCHECK_LE(static_cast<size_t>(file_type_index), + file_types->extensions.size()); + DCHECK_GE(file_type_index, 1); + [popup selectItemAtIndex:file_type_index - 1]; [handler popupAction:popup]; - } else { - [popup selectItemAtIndex:-1]; + } else if (!default_extension.empty() && default_extension_index != -1) { + [popup selectItemAtIndex:default_extension_index]; [dialog setAllowedFileTypes:@[ base::SysUTF8ToNSString(default_extension) ]]; + } else { + // Select the first item. + [popup selectItemAtIndex:0]; + [handler popupAction:popup]; } return handler; @@ -418,9 +362,16 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { return hasMultipleFileTypeChoices_; } +SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, + SelectFilePolicy* policy) { + return new SelectFileDialogImpl(listener, policy); +} + +} // namespace ui + @implementation SelectFileDialogBridge -- (id)initWithSelectFileDialogImpl:(SelectFileDialogImpl*)s { +- (id)initWithSelectFileDialogImpl:(ui::SelectFileDialogImpl*)s { if ((self = [super init])) { selectFileDialogImpl_ = s; } @@ -498,13 +449,3 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { } @end - -namespace ui { - -SelectFileDialog* CreateMacSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { - return new SelectFileDialogImpl(listener, policy); -} - -} // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog_mac_unittest.mm b/chromium/ui/shell_dialogs/select_file_dialog_mac_unittest.mm new file mode 100644 index 00000000000..a9ad635e1a1 --- /dev/null +++ b/chromium/ui/shell_dialogs/select_file_dialog_mac_unittest.mm @@ -0,0 +1,470 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ui/shell_dialogs/select_file_dialog_mac.h" + +#include <vector> + +#import "base/mac/foundation_util.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define EXPECT_EQ_BOOL(a, b) \ + EXPECT_EQ(static_cast<bool>(a), static_cast<bool>(b)) + +namespace { +const int kFileTypePopupTag = 1234; + +// Returns a vector containing extension descriptions for a given popup. +std::vector<base::string16> GetExtensionDescriptionList(NSPopUpButton* popup) { + std::vector<base::string16> extension_descriptions; + for (NSString* description in [popup itemTitles]) + extension_descriptions.push_back(base::SysNSStringToUTF16(description)); + return extension_descriptions; +} + +// Fake user event to select the item at the given |index| from the extension +// dropdown popup. +void SelectItemAtIndex(NSPopUpButton* popup, int index) { + [[popup menu] performActionForItemAtIndex:index]; +} + +// Returns the NSPopupButton associated with the given |panel|. +NSPopUpButton* GetPopup(NSSavePanel* panel) { + return [[panel accessoryView] viewWithTag:kFileTypePopupTag]; +} + +// Helper method to convert an array to a vector. +template <typename T, size_t N> +std::vector<T> GetVectorFromArray(const T (&data)[N]) { + return std::vector<T>(data, data + N); +} + +// Helper struct to hold arguments for the call to +// SelectFileDialogImpl::SelectFileImpl. +struct FileDialogArguments { + ui::SelectFileDialog::Type type; + base::string16 title; + base::FilePath default_path; + ui::SelectFileDialog::FileTypeInfo* file_types; + int file_type_index; + base::FilePath::StringType default_extension; + gfx::NativeWindow owning_window; + void* params; +}; + +// Helper method to return a FileDialogArguments struct initialized with +// appropriate default values. +FileDialogArguments GetDefaultArguments() { + return {ui::SelectFileDialog::SELECT_SAVEAS_FILE, + base::ASCIIToUTF16(""), + base::FilePath(), + nullptr, + 0, + "", + nullptr, + nullptr}; +} + +} // namespace + +namespace ui { +namespace test { + +// Helper test base to initialize SelectFileDialogImpl. +class SelectFileDialogMacTest : public testing::Test, + public SelectFileDialog::Listener { + public: + SelectFileDialogMacTest() + : dialog_(new SelectFileDialogImpl(this, nullptr)) {} + + // Overridden from SelectFileDialog::Listener. + void FileSelected(const base::FilePath& path, + int index, + void* params) override {} + + protected: + // Helper method to launch a dialog with the given |args|. + void SelectFileWithParams(FileDialogArguments args) { + dialog_->SelectFile(args.type, args.title, args.default_path, + args.file_types, args.file_type_index, + args.default_extension, args.owning_window, + args.params); + } + + // Returns the number of panels currently active. + size_t GetActivePanelCount() const { + return dialog_->dialog_data_map_.size(); + } + + // Returns one of the created NSSavePanel. If multiple SelectFile calls were + // made on the same |dialog_| object, any of the created NSSavePanel will be + // returned. + NSSavePanel* GetPanel() const { + DCHECK_GE(GetActivePanelCount(), 1lu); + return dialog_->dialog_data_map_.begin()->first; + } + + void ResetDialog() { dialog_ = new SelectFileDialogImpl(this, nullptr); } + + private: + scoped_refptr<SelectFileDialogImpl> dialog_; + + DISALLOW_COPY_AND_ASSIGN(SelectFileDialogMacTest); +}; + +// Verify that the extension popup has the correct description and changing the +// popup item changes the allowed file types. +TEST_F(SelectFileDialogMacTest, ExtensionPopup) { + const std::string extensions_arr[][2] = {{"html", "htm"}, {"jpeg", "jpg"}}; + const base::string16 extension_descriptions_arr[] = { + base::ASCIIToUTF16("Webpage"), base::ASCIIToUTF16("Image")}; + + SelectFileDialog::FileTypeInfo file_type_info; + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[0])); + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[1])); + file_type_info.extension_description_overrides = + GetVectorFromArray<base::string16>(extension_descriptions_arr); + file_type_info.include_all_files = false; + + FileDialogArguments args(GetDefaultArguments()); + args.file_types = &file_type_info; + + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + + NSPopUpButton* popup = GetPopup(panel); + EXPECT_TRUE(popup); + + // Check that the dropdown list created has the correct description. + const std::vector<base::string16> extension_descriptions = + GetExtensionDescriptionList(popup); + EXPECT_EQ(file_type_info.extension_description_overrides, + extension_descriptions); + + // Ensure other file types are not allowed. + EXPECT_FALSE([panel allowsOtherFileTypes]); + + // Check that the first item was selected, since a file_type_index of 0 was + // passed and no default extension was provided. + EXPECT_EQ(0, [popup indexOfSelectedItem]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"htm"]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"html"]); + EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"jpg"]); + + // Select the second item. + SelectItemAtIndex(popup, 1); + EXPECT_EQ(1, [popup indexOfSelectedItem]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpg"]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpeg"]); + EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"html"]); +} + +// Verify file_type_info.include_all_files argument is respected. +TEST_F(SelectFileDialogMacTest, IncludeAllFiles) { + const std::string extensions_arr[][2] = {{"html", "htm"}, {"jpeg", "jpg"}}; + const base::string16 extension_descriptions_arr[] = { + base::ASCIIToUTF16("Webpage"), base::ASCIIToUTF16("Image")}; + + SelectFileDialog::FileTypeInfo file_type_info; + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[0])); + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[1])); + file_type_info.extension_description_overrides = + GetVectorFromArray<base::string16>(extension_descriptions_arr); + file_type_info.include_all_files = true; + + FileDialogArguments args(GetDefaultArguments()); + args.file_types = &file_type_info; + + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + + NSPopUpButton* popup = GetPopup(panel); + EXPECT_TRUE(popup); + + // Check that the dropdown list created has the correct description. + const std::vector<base::string16> extension_descriptions = + GetExtensionDescriptionList(popup); + EXPECT_EQ(3lu, extension_descriptions.size()); + EXPECT_EQ(base::ASCIIToUTF16("Webpage"), extension_descriptions[0]); + EXPECT_EQ(base::ASCIIToUTF16("Image"), extension_descriptions[1]); + EXPECT_EQ(base::ASCIIToUTF16("All Files"), extension_descriptions[2]); + + // Ensure other file types are allowed. + EXPECT_TRUE([panel allowsOtherFileTypes]); + + // Select the last item i.e. All Files. + SelectItemAtIndex(popup, 2); + + // Ensure allowedFileTypes is set to nil, which means any file type can be + // used. + EXPECT_EQ(2, [popup indexOfSelectedItem]); + EXPECT_EQ(nil, [panel allowedFileTypes]); +} + +// Verify that file_type_index and default_extension arguments cause the +// appropriate extension group to be initially selected. +TEST_F(SelectFileDialogMacTest, InitialSelection) { + const std::string extensions_arr[][2] = {{"html", "htm"}, {"jpeg", "jpg"}}; + const base::string16 extension_descriptions_arr[] = { + base::ASCIIToUTF16("Webpage"), base::ASCIIToUTF16("Image")}; + + SelectFileDialog::FileTypeInfo file_type_info; + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[0])); + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[1])); + file_type_info.extension_description_overrides = + GetVectorFromArray<base::string16>(extension_descriptions_arr); + + FileDialogArguments args = GetDefaultArguments(); + args.file_types = &file_type_info; + + args.file_type_index = 2; + args.default_extension = "jpg"; + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + NSPopUpButton* popup = GetPopup(panel); + EXPECT_TRUE(popup); + // Verify that the file_type_index causes the second item to be initially + // selected. + EXPECT_EQ(1, [popup indexOfSelectedItem]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpg"]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpeg"]); + EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"html"]); + + ResetDialog(); + args.file_type_index = 0; + args.default_extension = "pdf"; + SelectFileWithParams(args); + panel = GetPanel(); + popup = GetPopup(panel); + EXPECT_TRUE(popup); + // Verify that the first item was selected, since the default extension passed + // was not present in the extension list. + EXPECT_EQ(0, [popup indexOfSelectedItem]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"html"]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"htm"]); + EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"pdf"]); + EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"jpeg"]); + + ResetDialog(); + args.file_type_index = 0; + args.default_extension = "jpg"; + SelectFileWithParams(args); + panel = GetPanel(); + popup = GetPopup(panel); + EXPECT_TRUE(popup); + // Verify that the extension group corresponding to the default extension is + // initially selected. + EXPECT_EQ(1, [popup indexOfSelectedItem]); + // The allowed file types should just contain the default extension. + EXPECT_EQ(1lu, [[panel allowedFileTypes] count]); + EXPECT_TRUE([[panel allowedFileTypes] containsObject:@"jpg"]); + EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"jpeg"]); + EXPECT_FALSE([[panel allowedFileTypes] containsObject:@"html"]); +} + +// Verify that an appropriate extension description is shown even if an empty +// extension description is passed for a given extension group. +TEST_F(SelectFileDialogMacTest, EmptyDescription) { + const std::string extensions_arr[][1] = {{"pdf"}, {"jpg"}, {"qqq"}}; + const base::string16 extension_descriptions_arr[] = { + base::ASCIIToUTF16(""), base::ASCIIToUTF16("Image"), + base::ASCIIToUTF16("")}; + + SelectFileDialog::FileTypeInfo file_type_info; + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[0])); + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[1])); + file_type_info.extensions.push_back( + GetVectorFromArray<std::string>(extensions_arr[2])); + file_type_info.extension_description_overrides = + GetVectorFromArray<base::string16>(extension_descriptions_arr); + + FileDialogArguments args(GetDefaultArguments()); + args.file_types = &file_type_info; + + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + NSPopUpButton* popup = GetPopup(panel); + EXPECT_TRUE(popup); + + // Check that the dropdown list created has the correct description. + const std::vector<base::string16> extension_descriptions = + GetExtensionDescriptionList(popup); + EXPECT_EQ(3lu, extension_descriptions.size()); + // Verify that the correct system description is produced for known file types + // like pdf if no extension description is provided by the client. + EXPECT_EQ(base::ASCIIToUTF16("Portable Document Format (PDF)"), + extension_descriptions[0]); + EXPECT_EQ(base::ASCIIToUTF16("Image"), extension_descriptions[1]); + // Verify the description for unknown file types if no extension description + // is provided by the client. + EXPECT_EQ(base::ASCIIToUTF16("QQQ File (.qqq)"), extension_descriptions[2]); +} + +// Verify that passing an empty extension list in file_type_info causes the All +// Files Option to display in the extension dropdown. +TEST_F(SelectFileDialogMacTest, EmptyExtension) { + SelectFileDialog::FileTypeInfo file_type_info; + + FileDialogArguments args(GetDefaultArguments()); + args.file_types = &file_type_info; + + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + NSPopUpButton* popup = GetPopup(panel); + EXPECT_TRUE(popup); + + const std::vector<base::string16> extension_descriptions = + GetExtensionDescriptionList(popup); + EXPECT_EQ(1lu, extension_descriptions.size()); + EXPECT_EQ(base::ASCIIToUTF16("All Files"), extension_descriptions[0]); + + // Ensure other file types are allowed. + EXPECT_TRUE([panel allowsOtherFileTypes]); +} + +// Verify that passing a null file_types value causes no extension dropdown to +// display. +TEST_F(SelectFileDialogMacTest, FileTypesNull) { + SelectFileWithParams(GetDefaultArguments()); + NSSavePanel* panel = GetPanel(); + EXPECT_TRUE([panel allowsOtherFileTypes]); + EXPECT_FALSE([panel accessoryView]); +} + +// Verify that appropriate properties are set on the NSSavePanel for different +// dialog types. +TEST_F(SelectFileDialogMacTest, SelectionType) { + SelectFileDialog::FileTypeInfo file_type_info; + FileDialogArguments args = GetDefaultArguments(); + args.file_types = &file_type_info; + + enum { + HAS_ACCESSORY_VIEW = 1, + PICK_FILES = 2, + PICK_DIRS = 4, + CREATE_DIRS = 8, + MULTIPLE_SELECTION = 16, + }; + + struct SelectionTypeTestCase { + SelectFileDialog::Type type; + unsigned options; + std::string prompt; + } test_cases[] = { + {SelectFileDialog::SELECT_FOLDER, PICK_DIRS | CREATE_DIRS, "Select"}, + {SelectFileDialog::SELECT_UPLOAD_FOLDER, PICK_DIRS | CREATE_DIRS, + "Upload"}, + {SelectFileDialog::SELECT_SAVEAS_FILE, HAS_ACCESSORY_VIEW | CREATE_DIRS, + "Save"}, + {SelectFileDialog::SELECT_OPEN_FILE, HAS_ACCESSORY_VIEW | PICK_FILES, + "Open"}, + {SelectFileDialog::SELECT_OPEN_MULTI_FILE, + HAS_ACCESSORY_VIEW | PICK_FILES | MULTIPLE_SELECTION, "Open"}, + }; + + for (size_t i = 0; i < arraysize(test_cases); i++) { + SCOPED_TRACE( + base::StringPrintf("i=%lu file_dialog_type=%d", i, test_cases[i].type)); + args.type = test_cases[i].type; + ResetDialog(); + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + + EXPECT_EQ_BOOL(test_cases[i].options & HAS_ACCESSORY_VIEW, + [panel accessoryView]); + EXPECT_EQ_BOOL(test_cases[i].options & CREATE_DIRS, + [panel canCreateDirectories]); + EXPECT_EQ(test_cases[i].prompt, base::SysNSStringToUTF8([panel prompt])); + + if (args.type != SelectFileDialog::SELECT_SAVEAS_FILE) { + NSOpenPanel* open_panel = base::mac::ObjCCast<NSOpenPanel>(panel); + // Verify that for types other than save file dialogs, an NSOpenPanel is + // created. + EXPECT_TRUE(open_panel); + EXPECT_EQ_BOOL(test_cases[i].options & PICK_FILES, + [open_panel canChooseFiles]); + EXPECT_EQ_BOOL(test_cases[i].options & PICK_DIRS, + [open_panel canChooseDirectories]); + EXPECT_EQ_BOOL(test_cases[i].options & MULTIPLE_SELECTION, + [open_panel allowsMultipleSelection]); + } + } +} + +// Verify that the correct message is set on the NSSavePanel. +TEST_F(SelectFileDialogMacTest, DialogMessage) { + const std::string test_title = "test title"; + FileDialogArguments args = GetDefaultArguments(); + args.title = base::ASCIIToUTF16(test_title); + SelectFileWithParams(args); + EXPECT_EQ(test_title, base::SysNSStringToUTF8([GetPanel() message])); +} + +// Verify that multiple file dialogs are corrected handled. +TEST_F(SelectFileDialogMacTest, MultipleDialogs) { + FileDialogArguments args(GetDefaultArguments()); + SelectFileWithParams(args); + SelectFileWithParams(args); + EXPECT_EQ(2lu, GetActivePanelCount()); + + // Verify closing the panel decreases the panel count. + NSSavePanel* panel = GetPanel(); + [panel cancel:panel]; + EXPECT_EQ(1lu, GetActivePanelCount()); + + panel = GetPanel(); + [panel ok:panel]; + EXPECT_EQ(0lu, GetActivePanelCount()); +} + +// Verify that the default_path argument is respected. +TEST_F(SelectFileDialogMacTest, DefaultPath) { + const std::string fake_path = "/fake_directory/filename.txt"; + FileDialogArguments args(GetDefaultArguments()); + args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path)); + + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + + EXPECT_EQ(args.default_path.DirName(), + base::mac::NSStringToFilePath([[panel directoryURL] path])); + EXPECT_EQ(args.default_path.BaseName(), + base::mac::NSStringToFilePath([panel nameFieldStringValue])); +} + +// Verify that the file dialog does not hide extension for filenames with +// multiple extensions. +TEST_F(SelectFileDialogMacTest, MultipleExtension) { + const std::string fake_path_normal = "/fake_directory/filename.tar"; + const std::string fake_path_multiple = "/fake_directory/filename.tar.gz"; + FileDialogArguments args(GetDefaultArguments()); + + args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_normal)); + SelectFileWithParams(args); + NSSavePanel* panel = GetPanel(); + EXPECT_TRUE([panel canSelectHiddenExtension]); + + ResetDialog(); + args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_multiple)); + SelectFileWithParams(args); + panel = GetPanel(); + EXPECT_FALSE([panel canSelectHiddenExtension]); + EXPECT_FALSE([panel isExtensionHidden]); +} + +} // namespace test +} // 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 40215a344fe..d455f59c5ce 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_win.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog_win.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <set> +#include <tuple> #include "base/bind.h" #include "base/files/file_path.h" @@ -17,7 +18,6 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/threading/thread.h" -#include "base/tuple.h" #include "base/win/registry.h" #include "base/win/scoped_comptr.h" #include "base/win/shortcut.h" @@ -28,9 +28,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/shell_dialogs_delegate.h" #include "ui/strings/grit/ui_strings.h" -#include "win8/viewer/metro_viewer_process_host.h" namespace { @@ -328,59 +326,6 @@ void SelectFileDialogImpl::SelectFileImpl( void* params) { has_multiple_file_type_choices_ = file_types ? file_types->extensions.size() > 1 : true; - // If the owning_window passed in is in metro then we need to forward the - // file open/save operations to metro. - if (GetShellDialogsDelegate() && - GetShellDialogsDelegate()->IsWindowInMetro(owning_window)) { - if (type == SELECT_SAVEAS_FILE) { - win8::MetroViewerProcessHost::HandleSaveFile( - title, - default_path, - GetFilterForFileTypes(file_types), - file_type_index, - default_extension, - base::Bind(&ui::SelectFileDialog::Listener::FileSelected, - base::Unretained(listener_)), - base::Bind(&ui::SelectFileDialog::Listener::FileSelectionCanceled, - base::Unretained(listener_))); - return; - } else if (type == SELECT_OPEN_FILE) { - win8::MetroViewerProcessHost::HandleOpenFile( - title, - default_path, - GetFilterForFileTypes(file_types), - base::Bind(&ui::SelectFileDialog::Listener::FileSelected, - base::Unretained(listener_)), - base::Bind(&ui::SelectFileDialog::Listener::FileSelectionCanceled, - base::Unretained(listener_))); - return; - } else if (type == SELECT_OPEN_MULTI_FILE) { - win8::MetroViewerProcessHost::HandleOpenMultipleFiles( - title, - default_path, - GetFilterForFileTypes(file_types), - base::Bind(&ui::SelectFileDialog::Listener::MultiFilesSelected, - base::Unretained(listener_)), - base::Bind(&ui::SelectFileDialog::Listener::FileSelectionCanceled, - base::Unretained(listener_))); - return; - } else if (type == SELECT_FOLDER || type == SELECT_UPLOAD_FOLDER) { - base::string16 title_string = title; - if (type == SELECT_UPLOAD_FOLDER && title_string.empty()) { - // If it's for uploading don't use default dialog title to - // make sure we clearly tell it's for uploading. - title_string = l10n_util::GetStringUTF16( - IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE); - } - win8::MetroViewerProcessHost::HandleSelectFolder( - title_string, - base::Bind(&ui::SelectFileDialog::Listener::FileSelected, - base::Unretained(listener_)), - base::Bind(&ui::SelectFileDialog::Listener::FileSelectionCanceled, - base::Unretained(listener_))); - return; - } - } HWND owner = owning_window && owning_window->GetRootWindow() ? owning_window->GetHost()->GetAcceleratedWidget() : NULL; @@ -512,12 +457,12 @@ bool SelectFileDialogImpl::SaveFileAsWithFilter( // Figure out what filter got selected. The filter index is 1-based. std::wstring filter_selected; if (*index > 0) { - std::vector<base::Tuple<base::string16, base::string16>> filters = + std::vector<std::tuple<base::string16, base::string16>> filters = ui::win::OpenFileName::GetFilters(save_as.GetOPENFILENAME()); if (*index > filters.size()) NOTREACHED() << "Invalid filter index."; else - filter_selected = base::get<1>(filters[*index - 1]); + filter_selected = std::get<1>(filters[*index - 1]); } // Get the extension that was suggested to the user (when the Save As dialog @@ -739,7 +684,7 @@ std::wstring AppendExtensionIfNeeded( if (!(filter_selected.empty() || filter_selected == L"*.*") && !base::win::RegKey(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ).Valid() && file_extension != suggested_ext) { - if (return_value[return_value.length() - 1] != L'.') + if (return_value.back() != L'.') return_value.append(L"."); return_value.append(suggested_ext); } @@ -761,9 +706,8 @@ SelectFileDialog* CreateWinSelectFileDialog( listener, policy, get_open_file_name_impl, get_save_file_name_impl); } -SelectFileDialog* CreateDefaultWinSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { +SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, + SelectFilePolicy* policy) { return CreateWinSelectFileDialog(listener, policy, base::Bind(&CallBuiltinGetOpenFileName), diff --git a/chromium/ui/shell_dialogs/select_file_dialog_win.h b/chromium/ui/shell_dialogs/select_file_dialog_win.h index 646bdb12b84..e066c65f1d3 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_win.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_win.h @@ -28,10 +28,6 @@ SHELL_DIALOGS_EXPORT SelectFileDialog* CreateWinSelectFileDialog( const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl, const base::Callback<bool(OPENFILENAME* ofn)>& get_save_file_name_impl); -SelectFileDialog* CreateDefaultWinSelectFileDialog( - SelectFileDialog::Listener* listener, - SelectFilePolicy* policy); - } // namespace ui #endif // UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_WIN_H_ diff --git a/chromium/ui/shell_dialogs/shell_dialog_linux.cc b/chromium/ui/shell_dialogs/shell_dialog_linux.cc new file mode 100644 index 00000000000..685369c2baa --- /dev/null +++ b/chromium/ui/shell_dialogs/shell_dialog_linux.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/shell_dialogs/shell_dialog_linux.h" + +namespace { + +ui::ShellDialogLinux* g_shell_dialog_linux = nullptr; + +} // namespace + +namespace ui { + +void ShellDialogLinux::SetInstance(ShellDialogLinux* instance) { + g_shell_dialog_linux = instance; +} + +const ShellDialogLinux* ShellDialogLinux::instance() { + return g_shell_dialog_linux; +} + +SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, + 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); +#endif + NOTIMPLEMENTED(); + return nullptr; +} + +} // namespace ui diff --git a/chromium/ui/shell_dialogs/linux_shell_dialog.h b/chromium/ui/shell_dialogs/shell_dialog_linux.h index a503b78ebdd..1dad34d15b5 100644 --- a/chromium/ui/shell_dialogs/linux_shell_dialog.h +++ b/chromium/ui/shell_dialogs/shell_dialog_linux.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_SHELL_DIALOGS_LINUX_SHELL_DIALOG_H_ -#define UI_SHELL_DIALOGS_LINUX_SHELL_DIALOG_H_ +#ifndef UI_SHELL_DIALOGS_SHELL_DIALOG_LINUX_H_ +#define UI_SHELL_DIALOGS_SHELL_DIALOG_LINUX_H_ #include "ui/shell_dialogs/select_file_dialog.h" #include "ui/shell_dialogs/shell_dialogs_export.h" @@ -12,21 +12,21 @@ namespace ui { // An interface that lets different Linux platforms override the // CreateSelectFileDialog function declared here to return native file dialogs. -class SHELL_DIALOGS_EXPORT LinuxShellDialog { +class SHELL_DIALOGS_EXPORT ShellDialogLinux { public: - virtual ~LinuxShellDialog() {} + virtual ~ShellDialogLinux() {} // Sets the dynamically loaded singleton that draws the desktop native // UI. This pointer is not owned, and if this method is called a second time, // the first instance is not deleted. - static void SetInstance(LinuxShellDialog* instance); + static void SetInstance(ShellDialogLinux* instance); // Returns a LinuxUI instance for the toolkit used in the user's desktop // environment. // // Can return NULL, in case no toolkit has been set. (For example, if we're // running with the "--ash" flag.) - static const LinuxShellDialog* instance(); + static const ShellDialogLinux* instance(); // Returns a native file selection dialog. virtual SelectFileDialog* CreateSelectFileDialog( @@ -36,5 +36,4 @@ class SHELL_DIALOGS_EXPORT LinuxShellDialog { } // namespace ui -#endif // UI_SHELL_DIALOGS_LINUX_SHELL_DIALOG_H_ - +#endif // UI_SHELL_DIALOGS_SHELL_DIALOG_LINUX_H_ diff --git a/chromium/ui/shell_dialogs/shell_dialogs.gyp b/chromium/ui/shell_dialogs/shell_dialogs.gyp index 0d541fc88e1..8bf623659fe 100644 --- a/chromium/ui/shell_dialogs/shell_dialogs.gyp +++ b/chromium/ui/shell_dialogs/shell_dialogs.gyp @@ -29,8 +29,6 @@ 'base_shell_dialog.h', 'base_shell_dialog_win.cc', 'base_shell_dialog_win.h', - 'linux_shell_dialog.cc', - 'linux_shell_dialog.h', 'select_file_dialog.cc', 'select_file_dialog.h', 'select_file_dialog_android.cc', @@ -45,6 +43,8 @@ 'select_file_policy.h', 'selected_file_info.cc', 'selected_file_info.h', + 'shell_dialog_linux.cc', + 'shell_dialog_linux.h', ], 'conditions': [ ['use_aura==1', @@ -74,13 +74,6 @@ }, } ], - ['OS=="win"', - { - 'dependencies': [ - '../../win8/win8.gyp:metro_viewer', - ], - } - ], ], }, # target_name: shell_dialogs { @@ -90,14 +83,34 @@ 'dependencies': [ '../../base/base.gyp:base', '../../base/base.gyp:test_support_base', - '../../base/base.gyp:run_all_unittests', '../../testing/gtest.gyp:gtest', + '../base/ui_base.gyp:ui_base', 'shell_dialogs', ], 'sources': [ # Note: file list duplicated in GN build. + 'run_all_unittests.cc', + 'select_file_dialog_mac_unittest.mm', 'select_file_dialog_win_unittest.cc', ], + 'conditions' : [ + ['OS=="mac"', + { + 'mac_bundle': 1, + 'mac_bundle_resources' : [ + '../../chrome/app/nibs/SaveAccessoryView.xib', + # The unittest expects a locale.pak file to exist in the bundle + # for English-US. Copy it in from where it was generated by + # ui_resources.gyp:ui_test_pak. + '<(PRODUCT_DIR)/ui/en.lproj/locale.pak', + ], + 'dependencies': [ + # Needed to generate locale.pak. + '../resources/ui_resources.gyp:ui_test_pak', + ], + } + ], + ], }, ], } diff --git a/chromium/ui/shell_dialogs/shell_dialogs_delegate.h b/chromium/ui/shell_dialogs/shell_dialogs_delegate.h deleted file mode 100644 index ca2c57cf2dc..00000000000 --- a/chromium/ui/shell_dialogs/shell_dialogs_delegate.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_DELEGATE_H_ -#define UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_DELEGATE_H_ - -#include "ui/gfx/native_widget_types.h" -#include "ui/shell_dialogs/shell_dialogs_export.h" - -namespace ui { - -class SHELL_DIALOGS_EXPORT ShellDialogsDelegate { - public: - virtual ~ShellDialogsDelegate() {} - - // Returns true if the window passed in is in the Windows 8 metro - // environment. - virtual bool IsWindowInMetro(gfx::NativeWindow window) = 0; -}; - -} // namespace ui - -#endif // UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_DELEGATE_H_ diff --git a/chromium/ui/snapshot/BUILD.gn b/chromium/ui/snapshot/BUILD.gn index c79a70b3aa8..dda02bc3a4f 100644 --- a/chromium/ui/snapshot/BUILD.gn +++ b/chromium/ui/snapshot/BUILD.gn @@ -70,7 +70,6 @@ test("snapshot_unittests") { deps = [ ":snapshot", "//base", - "//base/allocator", "//base/test:test_support", "//skia", "//testing/gtest", diff --git a/chromium/ui/snapshot/snapshot.gyp b/chromium/ui/snapshot/snapshot.gyp index ab78afe8aaa..340668c5c48 100644 --- a/chromium/ui/snapshot/snapshot.gyp +++ b/chromium/ui/snapshot/snapshot.gyp @@ -90,12 +90,6 @@ '../wm/wm.gyp:wm', ], }], - # See http://crbug.com/162998#c4 for why this is needed. - ['OS=="linux" and use_allocator!="none"', { - 'dependencies': [ - '../../base/allocator/allocator.gyp:allocator', - ], - }], ], }, ], diff --git a/chromium/ui/snapshot/snapshot_android.cc b/chromium/ui/snapshot/snapshot_android.cc index 0a123b9f2dc..fb7d40d47a6 100644 --- a/chromium/ui/snapshot/snapshot_android.cc +++ b/chromium/ui/snapshot/snapshot_android.cc @@ -41,8 +41,7 @@ static void MakeAsyncCopyRequest( scoped_ptr<cc::CopyOutputRequest> request = cc::CopyOutputRequest::CreateBitmapRequest(callback); - const gfx::Display& display = - gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); + const gfx::Display& display = gfx::Screen::GetScreen()->GetPrimaryDisplay(); float device_scale_factor = display.device_scale_factor(); gfx::Rect source_rect_in_pixel = gfx::ScaleToEnclosingRect(source_rect, device_scale_factor); diff --git a/chromium/ui/snapshot/snapshot_mac.mm b/chromium/ui/snapshot/snapshot_mac.mm index d1eb698b693..9761cb7f508 100644 --- a/chromium/ui/snapshot/snapshot_mac.mm +++ b/chromium/ui/snapshot/snapshot_mac.mm @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.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" @@ -26,7 +27,7 @@ bool GrabViewSnapshot(gfx::NativeView view, // Get the view bounds relative to the screen NSRect frame = [view convertRect:[view bounds] toView:nil]; - frame.origin = [window convertBaseToScreen:frame.origin]; + frame = [window convertRectToScreen:frame]; gfx::Rect view_bounds = gfx::Rect(NSRectToCGRect(frame)); diff --git a/chromium/ui/strings/translations/ui_strings_bn.xtb b/chromium/ui/strings/translations/ui_strings_bn.xtb index ba863f32c65..edb68ba36cb 100644 --- a/chromium/ui/strings/translations/ui_strings_bn.xtb +++ b/chromium/ui/strings/translations/ui_strings_bn.xtb @@ -15,7 +15,7 @@ <translation id="1781701194097416995">শব্দকে বামদিকে সরান</translation> <translation id="1801827354178857021">পূর্ণচ্ছেদ</translation> <translation id="1809410197924942083"><ph name="QUANTITY" /> MB/s</translation> -<translation id="1830179671306812954">{HOURS,plural, =1{১ ঘন্টা এবং }one{# ঘন্টা এবং }other{# ঘন্টা এবং }}</translation> +<translation id="1830179671306812954">{HOURS,plural, =1{১ ঘণ্টা এবং }one{# ঘণ্টা এবং }other{# ঘণ্টা এবং }}</translation> <translation id="1860796786778352021">বিজ্ঞপ্তি বন্ধ করা হয়েছে</translation> <translation id="1871244248791675517">Ins</translation> <translation id="1901303067676059328">&সকল নির্বাচন করুন</translation> @@ -32,7 +32,7 @@ <translation id="2557207087669398617">লাইনের শুরু পর্যন্ত সরান</translation> <translation id="2666092431469916601">শীর্ষ</translation> <translation id="2704295676501803339">বামদিকে সরান</translation> -<translation id="2743387203779672305">ক্লিপবোর্ডে অনুলিপি করুন</translation> +<translation id="2743387203779672305">ক্লিপবোর্ডে কপি করুন</translation> <translation id="2803313416453193357">ফোল্ডার খুলুন</translation> <translation id="2983818520079887040">সেটিংস...</translation> <translation id="3036649622769666520">খোলা ফাইল</translation> @@ -78,7 +78,7 @@ <translation id="5768079895599174203">{DAYS,plural, =1{১ দিন এবং }one{# দিন এবং }other{# দিন এবং }}</translation> <translation id="5906667377645263094">{SECONDS,plural, =1{১ সেকেন্ড বাকি}one{# সেকেন্ড বাকি}other{# সেকেন্ড বাকি}}</translation> <translation id="5941711191222866238">ছোট করুন</translation> -<translation id="5943826764092288734">{HOURS,plural, =1{১ ঘণ্টা}one{# ঘন্টা}other{# ঘন্টা}}</translation> +<translation id="5943826764092288734">{HOURS,plural, =1{১ ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}</translation> <translation id="5948410903763073882">Alt+<ph name="KEY_COMBO_NAME" /></translation> <translation id="598033046487663418">সমস্ত অ্যাপ্লিকেশান</translation> <translation id="6040143037577758943">বন্ধ</translation> @@ -98,7 +98,7 @@ <translation id="6808150112686056157">মিডিয়া থামান</translation> <translation id="6829324100069873704">বিজ্ঞপ্তিগুলিতে ফিরে যান</translation> <translation id="6845383723252244143">ফোল্ডার নির্বাচন করুন</translation> -<translation id="6863590663815976734">{HOURS,plural, =1{১ ঘন্টা বাকি}one{# ঘন্টা বাকি}other{# ঘন্টা বাকি}}</translation> +<translation id="6863590663815976734">{HOURS,plural, =1{১ ঘণ্টা বাকি}one{# ঘণ্টা বাকি}other{# ঘণ্টা বাকি}}</translation> <translation id="688711909580084195">শিরোনামহীন ওয়েবপৃষ্ঠা</translation> <translation id="6903282483217634857">ডানদিকে সরান</translation> <translation id="6907759265145635167"><ph name="QUANTITY" /> PB/s</translation> @@ -121,7 +121,7 @@ <translation id="7850320739366109486">বিরক্ত করবেন না</translation> <translation id="7907591526440419938">খোলা ফাইল</translation> <translation id="7960078400008666149">এক ঘন্টার জন্য বিরক্ত করবেন না</translation> -<translation id="8106081041558092062">{HOURS,plural, =1{১ ঘন্টা পূর্বে}one{# ঘন্টা পূর্বে}other{# ঘন্টা পূর্বে}}</translation> +<translation id="8106081041558092062">{HOURS,plural, =1{১ ঘণ্টা পূর্বে}one{# ঘণ্টা পূর্বে}other{# ঘণ্টা পূর্বে}}</translation> <translation id="8131263257437993507">{SECONDS,plural, =1{১ সেকেন্ড বাকি}one{# সেকেন্ড বাকি}other{# সেকেন্ড বাকি}}</translation> <translation id="815598010540052116">নীচে স্ক্রোল করুন</translation> <translation id="8179976553408161302">Enter</translation> diff --git a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb index 075fdbb88aa..4bbe7193bf6 100644 --- a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb +++ b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb @@ -144,6 +144,6 @@ <translation id="9044832324875206639">{SECONDS,plural, =1{1 秒}other{# 秒}}</translation> <translation id="9170848237812810038">撤消(&U)</translation> <translation id="928465423150706909">移至行末</translation> -<translation id="932327136139879170">家庭</translation> +<translation id="932327136139879170">首页</translation> <translation id="945522503751344254">发送反馈</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/ui/strings/ui_strings.grd b/chromium/ui/strings/ui_strings.grd index 5e159dd6f5c..59487442d94 100644 --- a/chromium/ui/strings/ui_strings.grd +++ b/chromium/ui/strings/ui_strings.grd @@ -9,7 +9,8 @@ need to be translated for each locale.--> <outputs> <!-- TODO add each of your output files. Modify the three below, and add your own for your various languages. See the user's guide - (http://wiki/Main/GritUsersGuide) for more details. + (https://www.chromium.org/developers/tools-we-use-in-chromium/grit/grit-users-guide) + for more details. Note that all output references are relative to the output directory which is specified at build time. --> <output filename="grit/ui_strings.h" type="rc_header"> @@ -592,11 +593,9 @@ need to be translated for each locale.--> </message> <!-- Message center --> - <if expr="use_ash"> - <message name="IDS_MESSAGE_CENTER_ACCESSIBLE_NAME" desc="The accessible name for the Notification Center window."> - Notification Center - </message> - </if> + <message name="IDS_MESSAGE_CENTER_ACCESSIBLE_NAME" desc="The accessible name for the Notification Center window."> + Notification Center + </message> <message name="IDS_MESSAGE_CENTER_NOTIFIER_DISABLE" desc="The menu entry for disabling a notifier from a notification."> Disable notifications from <ph name="notifier_name">$1<ex>Notification Galore!</ex></ph> </message> diff --git a/chromium/ui/surface/BUILD.gn b/chromium/ui/surface/BUILD.gn index 96130c44d8e..b583c8bee71 100644 --- a/chromium/ui/surface/BUILD.gn +++ b/chromium/ui/surface/BUILD.gn @@ -6,8 +6,6 @@ import("//build/config/ui.gni") component("surface") { sources = [ - "accelerated_surface_mac.cc", - "accelerated_surface_mac.h", "surface_export.h", "transport_dib.cc", "transport_dib.h", @@ -27,12 +25,4 @@ component("surface") { "//ui/gfx/geometry", "//ui/gl", ] - - if (is_mac) { - # Required by accelerated_surface_mac.cc. - libs = [ - "IOSurface.framework", - "OpenGL.framework", - ] - } } diff --git a/chromium/ui/surface/OWNERS b/chromium/ui/surface/OWNERS index 62a309c13fc..8e808054fc0 100644 --- a/chromium/ui/surface/OWNERS +++ b/chromium/ui/surface/OWNERS @@ -2,4 +2,3 @@ jbauman@chromium.org kbr@chromium.org piman@chromium.org pinkerton@chromium.org -stuartmorgan@chromium.org diff --git a/chromium/ui/surface/accelerated_surface_mac.cc b/chromium/ui/surface/accelerated_surface_mac.cc deleted file mode 100644 index 630ee7b3271..00000000000 --- a/chromium/ui/surface/accelerated_surface_mac.cc +++ /dev/null @@ -1,266 +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/surface/accelerated_surface_mac.h" - -#include "base/logging.h" -#include "base/mac/scoped_cftyperef.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_implementation.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/scoped_make_current.h" - -// Note that this must be included after gl_bindings.h to avoid conflicts. -#include <OpenGL/CGLIOSurface.h> - -AcceleratedSurface::AcceleratedSurface() - : io_surface_id_(0), - allocate_fbo_(false), - texture_(0), - fbo_(0) { -} - -AcceleratedSurface::~AcceleratedSurface() {} - -bool AcceleratedSurface::Initialize( - gfx::GLContext* share_context, - bool allocate_fbo, - gfx::GpuPreference gpu_preference) { - allocate_fbo_ = allocate_fbo; - - // GL should be initialized by content::SupportsCoreAnimationPlugins(). - DCHECK_NE(gfx::GetGLImplementation(), gfx::kGLImplementationNone); - - // Drawing to IOSurfaces via OpenGL only works with Apple's GL and - // not with the OSMesa software renderer. - if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL && - gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL) - return false; - - gl_surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(1, 1)); - if (!gl_surface_.get()) { - Destroy(); - return false; - } - - gfx::GLShareGroup* share_group = - share_context ? share_context->share_group() : NULL; - - gl_context_ = gfx::GLContext::CreateGLContext( - share_group, - gl_surface_.get(), - gpu_preference); - if (!gl_context_.get()) { - Destroy(); - return false; - } - - // Now we're ready to handle SetSurfaceSize calls, which will - // allocate and/or reallocate the IOSurface and associated offscreen - // OpenGL structures for rendering. - return true; -} - -void AcceleratedSurface::Destroy() { - // The FBO and texture objects will be destroyed when the OpenGL context, - // and any other contexts sharing resources with it, is. We don't want to - // make the context current one last time here just in order to delete - // these objects. - gl_context_ = NULL; - gl_surface_ = NULL; -} - -// Call after making changes to the surface which require a visual update. -// Makes the rendering show up in other processes. -void AcceleratedSurface::SwapBuffers() { - if (io_surface_.get() != NULL) { - if (allocate_fbo_) { - // Bind and unbind the framebuffer to make changes to the - // IOSurface show up in the other process. - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); - glFlush(); - } else { - // Copy the current framebuffer's contents into our "live" texture. - // Note that the current GL context might not be ours at this point! - // This is deliberate, so that surrounding code using GL can produce - // rendering results consumed by the AcceleratedSurface. - // Need to save and restore OpenGL state around this call. - GLint current_texture = 0; - GLenum target_binding = GL_TEXTURE_BINDING_RECTANGLE_ARB; - GLenum target = GL_TEXTURE_RECTANGLE_ARB; - glGetIntegerv(target_binding, ¤t_texture); - glBindTexture(target, texture_); - glCopyTexSubImage2D(target, 0, - 0, 0, - 0, 0, - real_surface_size_.width(), - real_surface_size_.height()); - glBindTexture(target, current_texture); - // This flush is absolutely essential -- it guarantees that the - // rendering results are seen by the other process. - glFlush(); - } - } -} - -static void AddBooleanValue(CFMutableDictionaryRef dictionary, - const CFStringRef key, - bool value) { - CFDictionaryAddValue(dictionary, key, - (value ? kCFBooleanTrue : kCFBooleanFalse)); -} - -static void AddIntegerValue(CFMutableDictionaryRef dictionary, - const CFStringRef key, - int32_t value) { - base::ScopedCFTypeRef<CFNumberRef> number( - CFNumberCreate(NULL, kCFNumberSInt32Type, &value)); - CFDictionaryAddValue(dictionary, key, number.get()); -} - -// Creates a new OpenGL texture object bound to the given texture target. -// Caller owns the returned texture. -static GLuint CreateTexture(GLenum target) { - GLuint texture = 0; - glGenTextures(1, &texture); - glBindTexture(target, texture); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - return texture; -} - -void AcceleratedSurface::AllocateRenderBuffers(GLenum target, - const gfx::Size& size) { - if (!texture_) { - // Generate the texture object. - texture_ = CreateTexture(target); - // Generate and bind the framebuffer object. - glGenFramebuffersEXT(1, &fbo_); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); - } - - // Make sure that subsequent set-up code affects the render texture. - glBindTexture(target, texture_); -} - -bool AcceleratedSurface::SetupFrameBufferObject(GLenum target) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); - GLenum fbo_status; - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, - GL_COLOR_ATTACHMENT0_EXT, - target, - texture_, - 0); - fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT; -} - -gfx::Size AcceleratedSurface::ClampToValidDimensions(const gfx::Size& size) { - return gfx::Size(std::max(size.width(), 1), std::max(size.height(), 1)); -} - -bool AcceleratedSurface::MakeCurrent() { - if (!gl_context_.get()) - return false; - return gl_context_->MakeCurrent(gl_surface_.get()); -} - -void AcceleratedSurface::Clear(const gfx::Rect& rect) { - DCHECK(gl_context_->IsCurrent(gl_surface_.get())); - glClearColor(0, 0, 0, 0); - glViewport(0, 0, rect.width(), rect.height()); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, rect.width(), 0, rect.height(), -1, 1); - glClear(GL_COLOR_BUFFER_BIT); -} - -uint32_t AcceleratedSurface::SetSurfaceSize(const gfx::Size& size) { - if (surface_size_ == size) { - // Return 0 to indicate to the caller that no new backing store - // allocation occurred. - return 0; - } - - // Only support IO surfaces if the GL implementation is the native desktop GL. - // IO surfaces will not work with, for example, OSMesa software renderer - // GL contexts. - if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL) - return 0; - - ui::ScopedMakeCurrent make_current(gl_context_.get(), gl_surface_.get()); - if (!make_current.Succeeded()) - return 0; - - gfx::Size clamped_size = ClampToValidDimensions(size); - - // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on - // Mac OS X and is required for IOSurface interoperability. - GLenum target = GL_TEXTURE_RECTANGLE_ARB; - if (allocate_fbo_) { - AllocateRenderBuffers(target, clamped_size); - } else if (!texture_) { - // Generate the texture object. - texture_ = CreateTexture(target); - } - - // Allocate a new IOSurface, which is the GPU resource that can be - // shared across processes. - base::ScopedCFTypeRef<CFMutableDictionaryRef> properties; - properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - AddIntegerValue(properties, kIOSurfaceWidth, clamped_size.width()); - AddIntegerValue(properties, kIOSurfaceHeight, clamped_size.height()); - AddIntegerValue(properties, kIOSurfaceBytesPerElement, 4); - AddBooleanValue(properties, kIOSurfaceIsGlobal, true); - // I believe we should be able to unreference the IOSurfaces without - // synchronizing with the browser process because they are - // ultimately reference counted by the operating system. - io_surface_.reset(IOSurfaceCreate(properties)); - - // Don't think we need to identify a plane. - GLuint plane = 0; - CGLError error = CGLTexImageIOSurface2D( - static_cast<CGLContextObj>(gl_context_->GetHandle()), - target, - GL_RGBA, - clamped_size.width(), - clamped_size.height(), - GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, - io_surface_.get(), - plane); - if (error != kCGLNoError) { - DLOG(ERROR) << "CGL error " << error << " during CGLTexImageIOSurface2D"; - } - if (allocate_fbo_) { - // Set up the frame buffer object. - if (!SetupFrameBufferObject(target)) { - DLOG(ERROR) << "Failed to set up frame buffer object"; - } - } - surface_size_ = size; - real_surface_size_ = clamped_size; - - // Now send back an identifier for the IOSurface. We originally - // intended to send back a mach port from IOSurfaceCreateMachPort - // but it looks like Chrome IPC would need to be modified to - // properly send mach ports between processes. For the time being we - // make our IOSurfaces global and send back their identifiers. On - // the browser process side the identifier is reconstituted into an - // IOSurface for on-screen rendering. - io_surface_id_ = IOSurfaceGetID(io_surface_); - return io_surface_id_; -} - -uint32_t AcceleratedSurface::GetSurfaceId() { - return io_surface_id_; -} diff --git a/chromium/ui/surface/accelerated_surface_mac.h b/chromium/ui/surface/accelerated_surface_mac.h deleted file mode 100644 index 9defa33a9a9..00000000000 --- a/chromium/ui/surface/accelerated_surface_mac.h +++ /dev/null @@ -1,147 +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_SURFACE_ACCELERATED_SURFACE_MAC_H_ -#define UI_SURFACE_ACCELERATED_SURFACE_MAC_H_ - -#include <CoreFoundation/CoreFoundation.h> -#include <IOSurface/IOSurface.h> -#include <stdint.h> - -#include "base/callback.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/memory/scoped_ptr.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/gpu_preference.h" -#include "ui/surface/surface_export.h" - -// Should not include GL headers in a header file. Forward declare these types -// instead. -typedef struct _CGLContextObject* CGLContextObj; -typedef unsigned int GLenum; -typedef unsigned int GLuint; - -namespace gfx { -class Rect; -} - -// Encapsulates an accelerated GL surface that can be shared across processes -// on systems that support it (10.6 and above). - -class SURFACE_EXPORT AcceleratedSurface { - public: - AcceleratedSurface(); - virtual ~AcceleratedSurface(); - - // Set up internal buffers. |share_context|, if non-NULL, is a context - // with which the internally created OpenGL context shares textures and - // other resources. |allocate_fbo| indicates whether or not this surface - // should allocate an offscreen frame buffer object (FBO) internally. If - // not, then the user is expected to allocate one. NOTE that allocating - // an FBO internally does NOT work properly with client code which uses - // OpenGL (i.e., via GLES2 command buffers), because the GLES2 - // implementation does not know to bind the accelerated surface's - // internal FBO when the default FBO is bound. |gpu_preference| indicates - // the GPU preference for the internally allocated GLContext. If - // |share_context| is non-NULL, then on platforms supporting dual GPUs, - // its GPU preference must match the passed one. Returns false upon - // failure. - bool Initialize(gfx::GLContext* share_context, - bool allocate_fbo, - gfx::GpuPreference gpu_preference); - // Tear down. Must be called before destructor to prevent leaks. - void Destroy(); - - // These methods are used only once the accelerated surface is initialized. - - // Sets the accelerated surface to the given size, creating a new one if - // the height or width changes. Returns a unique id of the IOSurface to - // which the surface is bound, or 0 if no changes were made or an error - // occurred. MakeCurrent() will have been called on the new surface. - uint32_t SetSurfaceSize(const gfx::Size& size); - - // Returns the id of this surface's IOSurface. - uint32_t GetSurfaceId(); - - // Sets the GL context to be the current one for drawing. Returns true if - // it succeeded. - bool MakeCurrent(); - // Clear the surface to be transparent. Assumes the caller has already called - // MakeCurrent(). - void Clear(const gfx::Rect& rect); - // Call after making changes to the surface which require a visual update. - // Makes the rendering show up in other processes. Assumes the caller has - // already called MakeCurrent(). - // - // If this AcceleratedSurface is configured with its own FBO, then - // this call causes the color buffer to be transmitted. Otherwise, - // it causes the frame buffer of the current GL context to be copied - // into an internal texture via glCopyTexSubImage2D. - // - // The size of the rectangle copied is the size last specified via - // SetSurfaceSize. If another GL context than the one this - // AcceleratedSurface contains is responsible for the production of - // the pixels, then when this entry point is called, the color - // buffer must be in a state where a glCopyTexSubImage2D is - // legal. (For example, if using multisampled FBOs, the FBO must - // have been resolved into a non-multisampled color texture.) - // Additionally, in this situation, the contexts must share - // server-side GL objects, so that this AcceleratedSurface's texture - // is a legal name in the namespace of the current context. - void SwapBuffers(); - - CGLContextObj context() { - return static_cast<CGLContextObj>(gl_context_->GetHandle()); - } - - // Get the accelerated surface size. - gfx::Size GetSize() const { return surface_size_; } - - private: - // Helper function to generate names for the backing texture and FBO. On - // return, the resulting names can be attached to |fbo_|. |target| is - // the target type for the color buffer. - void AllocateRenderBuffers(GLenum target, const gfx::Size& size); - - // Helper function to attach the buffers previously allocated by a call to - // AllocateRenderBuffers(). On return, |fbo_| can be used for - // rendering. |target| must be the same value as used in the call to - // AllocateRenderBuffers(). Returns |true| if the resulting framebuffer - // object is valid. - bool SetupFrameBufferObject(GLenum target); - - gfx::Size ClampToValidDimensions(const gfx::Size& size); - - // The OpenGL context, and pbuffer drawable, used to transfer data - // to the shared region (IOSurface). - scoped_refptr<gfx::GLSurface> gl_surface_; - scoped_refptr<gfx::GLContext> gl_context_; - base::ScopedCFTypeRef<IOSurfaceRef> io_surface_; - - // The id of |io_surface_| or 0 if that's NULL. - uint32_t io_surface_id_; - - gfx::Size surface_size_; - // It's important to avoid allocating zero-width or zero-height - // IOSurfaces and textures on the Mac, so we clamp each to a minimum - // of 1. This is the real size of the surface; surface_size_ is what - // the user requested. - gfx::Size real_surface_size_; - // TODO(kbr): the FBO management should not be in this class at all. - // However, if it is factored out, care needs to be taken to not - // introduce another copy of the color data on the GPU; the direct - // binding of the internal texture to the IOSurface saves a copy. - bool allocate_fbo_; - // This texture object is always allocated, regardless of whether - // the user requests an FBO be allocated. - GLuint texture_; - // The FBO and renderbuffer are only allocated if allocate_fbo_ is - // true. - GLuint fbo_; -}; - -#endif // UI_SURFACE_ACCELERATED_SURFACE_MAC_H_ diff --git a/chromium/ui/surface/surface.gyp b/chromium/ui/surface/surface.gyp index a219473e402..e45e2ae888a 100644 --- a/chromium/ui/surface/surface.gyp +++ b/chromium/ui/surface/surface.gyp @@ -13,15 +13,6 @@ '../../third_party/khronos', ], }], - ['OS == "mac"', { - # Required by accelerated_surface_mac.cc. - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/IOSurface.framework', - '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', - ], - }, - }], ], }, 'targets': [ @@ -37,8 +28,6 @@ '../gl/gl.gyp:gl', ], 'sources': [ - 'accelerated_surface_mac.cc', - 'accelerated_surface_mac.h', 'surface_export.h', 'transport_dib.cc', 'transport_dib.h', diff --git a/chromium/ui/surface/transport_dib_posix.cc b/chromium/ui/surface/transport_dib_posix.cc index be1a37363aa..aada735b530 100644 --- a/chromium/ui/surface/transport_dib_posix.cc +++ b/chromium/ui/surface/transport_dib_posix.cc @@ -57,8 +57,7 @@ bool TransportDIB::is_valid_handle(Handle dib) { return base::SharedMemory::IsHandleValid(dib); } -skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h, - bool opaque) { +SkCanvas* TransportDIB::GetPlatformCanvas(int w, int h, bool opaque) { if ((!memory() && !Map()) || !VerifyCanvasSize(w, h)) return NULL; return skia::CreatePlatformCanvas(w, h, opaque, diff --git a/chromium/ui/surface/transport_dib_win.cc b/chromium/ui/surface/transport_dib_win.cc index 82eabf87878..f002f55536d 100644 --- a/chromium/ui/surface/transport_dib_win.cc +++ b/chromium/ui/surface/transport_dib_win.cc @@ -58,7 +58,7 @@ bool TransportDIB::is_valid_handle(Handle dib) { return dib.IsValid(); } -skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h, +SkCanvas* TransportDIB::GetPlatformCanvas(int w, int h, bool opaque) { // This DIB already mapped the file into this process, but PlatformCanvas // will map it again. @@ -67,7 +67,7 @@ skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h, // We can't check the canvas size before mapping, but it's safe because // Windows will fail to map the section if the dimensions of the canvas // are too large. - skia::PlatformCanvas* canvas = skia::CreatePlatformCanvas( + SkCanvas* canvas = skia::CreatePlatformCanvas( w, h, opaque, shared_memory_.handle().GetHandle(), skia::RETURN_NULL_ON_FAILURE); diff --git a/chromium/ui/touch_selection/BUILD.gn b/chromium/ui/touch_selection/BUILD.gn index 4d69e189228..cc273875a43 100644 --- a/chromium/ui/touch_selection/BUILD.gn +++ b/chromium/ui/touch_selection/BUILD.gn @@ -68,15 +68,6 @@ static_library("test_support") { ] } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("ui_touch_selection_unittests_run") { - testonly = true - deps = [ - ":ui_touch_selection_unittests", - ] -} - test("ui_touch_selection_unittests") { sources = [ "longpress_drag_selector_unittest.cc", diff --git a/chromium/ui/touch_selection/ui_touch_selection_unittests_apk.isolate b/chromium/ui/touch_selection/ui_touch_selection_unittests_apk.isolate index a2b39ac5d96..e49c7076f50 100644 --- a/chromium/ui/touch_selection/ui_touch_selection_unittests_apk.isolate +++ b/chromium/ui/touch_selection/ui_touch_selection_unittests_apk.isolate @@ -8,6 +8,7 @@ 'variables': { 'command': [ '<(PRODUCT_DIR)/bin/run_ui_touch_selection_unittests', + '--logcat-output-dir', '${ISOLATED_OUTDIR}/logcats', ], 'files': [ '<(PRODUCT_DIR)/bin/run_ui_touch_selection_unittests', diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn index d506603f175..5b85b89b3cf 100644 --- a/chromium/ui/views/BUILD.gn +++ b/chromium/ui/views/BUILD.gn @@ -47,6 +47,7 @@ component("views") { "//ui/base", "//ui/base/ime", "//ui/compositor", + "//ui/display", "//ui/events", "//ui/events:events_base", "//ui/events/platform", @@ -84,6 +85,7 @@ component("views") { "dwmapi.lib", "imm32.lib", "oleacc.lib", + "wtsapi32.lib", ] ldflags = [ "/DELAYLOAD:user32.dll" ] deps += [ @@ -114,7 +116,7 @@ component("views") { "//ui/touch_selection", "//ui/wm", ] - if (!is_chromeos && !is_android) { + if (!is_chromeos) { sources += gypi_values.views_desktop_aura_sources if (use_x11) { sources += gypi_values.views_desktop_aura_x11_sources @@ -128,9 +130,6 @@ component("views") { sources += gypi_values.views_desktop_aura_linux_sources } } - if (is_android) { - sources += gypi_values.views_android_sources - } } if (is_mac) { @@ -163,6 +162,7 @@ source_set("test_support_internal") { "//skia", "//testing/gtest", "//ui/base", + "//ui/base:test_support", "//ui/base/ime", "//ui/compositor", "//ui/compositor:test_support", @@ -191,7 +191,10 @@ source_set("test_support_internal") { deps += [ "//ui/gfx/x" ] } if (use_ozone || !use_x11) { - sources -= [ "test/x11_property_change_waiter.cc" ] + sources -= [ + "test/x11_property_change_waiter.cc", + "test/x11_property_change_waiter.h", + ] } } @@ -202,6 +205,7 @@ static_library("test_support") { ] sources = [ "test/default_platform_test_helper.cc", + "test/native_widget_factory_desktop.cc", ] } @@ -212,7 +216,6 @@ test("views_unittests") { ":test_support", "//base", "//base:i18n", - "//base/allocator", "//base/test:test_support", "//cc", "//skia", @@ -230,13 +233,17 @@ test("views_unittests") { "//ui/gfx:test_support", "//ui/gfx/geometry", "//ui/gl:test_support", + "//ui/native_theme", "//ui/resources", + "//ui/resources:ui_test_pak", "//ui/strings", "//url", ] - data_deps = [ - "//ui/resources:ui_test_pak", + # TODO(thakis): This should be a data_deps on //ui/resources:ui_test_pak, but + # that has no effect. (See similar TODOs elsewhere ui_test.pak is listed) + data = [ + "$root_out_dir/ui_test.pak", ] if (is_win) { diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc index 3d4850e0bc2..9bd021e1d00 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc @@ -6,6 +6,7 @@ #include "base/memory/singleton.h" #include "base/stl_util.h" +#include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/views/accessibility/ax_aura_obj_wrapper.h" #include "ui/views/accessibility/ax_view_obj_wrapper.h" @@ -30,18 +31,27 @@ AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(Widget* widget) { } AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(aura::Window* window) { + if (!focus_client_) { + aura::Window* root_window = window->GetRootWindow(); + if (root_window) { + focus_client_ = aura::client::GetFocusClient(root_window); + root_window->AddObserver(this); + if (focus_client_) + focus_client_->AddObserver(this); + } + } return CreateInternal<AXWindowObjWrapper>(window, window_to_id_map_); } -int32_t AXAuraObjCache::GetID(View* view) { +int32_t AXAuraObjCache::GetID(View* view) const { return GetIDInternal(view, view_to_id_map_); } -int32_t AXAuraObjCache::GetID(Widget* widget) { +int32_t AXAuraObjCache::GetID(Widget* widget) const { return GetIDInternal(widget, widget_to_id_map_); } -int32_t AXAuraObjCache::GetID(aura::Window* window) { +int32_t AXAuraObjCache::GetID(aura::Window* window) const { return GetIDInternal(window, window_to_id_map_); } @@ -97,7 +107,17 @@ void AXAuraObjCache::GetTopLevelWindows( } } -AXAuraObjCache::AXAuraObjCache() : current_id_(1), is_destroying_(false) { +AXAuraObjWrapper* AXAuraObjCache::GetFocus() { + View* focused_view = GetFocusedView(); + if (focused_view) + return GetOrCreate(focused_view); + return nullptr; +} + +AXAuraObjCache::AXAuraObjCache() + : current_id_(1), + focus_client_(nullptr), + is_destroying_(false) { } AXAuraObjCache::~AXAuraObjCache() { @@ -106,6 +126,44 @@ AXAuraObjCache::~AXAuraObjCache() { cache_.clear(); } +View* AXAuraObjCache::GetFocusedView() { + if (!focus_client_) + return nullptr; + + aura::Window* focused_window = focus_client_->GetFocusedWindow(); + if (!focused_window) + return nullptr; + + Widget* focused_widget = Widget::GetWidgetForNativeView(focused_window); + while (!focused_widget) { + focused_window = focused_window->parent(); + if (!focused_window) + break; + + focused_widget = Widget::GetWidgetForNativeView(focused_window); + } + + if (!focused_widget) + return nullptr; + + FocusManager* focus_manager = focused_widget->GetFocusManager(); + if (!focus_manager) + return nullptr; + + return focus_manager->GetFocusedView(); +} + +void AXAuraObjCache::OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) { + View* view = GetFocusedView(); + if (view) + view->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); +} + +void AXAuraObjCache::OnWindowDestroying(aura::Window* window) { + focus_client_ = nullptr; +} + template <typename AuraViewWrapper, typename AuraView> AXAuraObjWrapper* AXAuraObjCache::CreateInternal( AuraView* aura_view, @@ -129,13 +187,11 @@ AXAuraObjWrapper* AXAuraObjCache::CreateInternal( template <typename AuraView> int32_t AXAuraObjCache::GetIDInternal( AuraView* aura_view, - std::map<AuraView*, int32_t>& aura_view_to_id_map) { + const std::map<AuraView*, int32_t>& aura_view_to_id_map) const { if (!aura_view) return -1; - typename std::map<AuraView*, int32_t>::iterator it = - aura_view_to_id_map.find(aura_view); - + auto it = aura_view_to_id_map.find(aura_view); if (it != aura_view_to_id_map.end()) return it->second; diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.h b/chromium/ui/views/accessibility/ax_aura_obj_cache.h index a7bb50c99b4..763f8f99171 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache.h +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.h @@ -11,6 +11,8 @@ #include <vector> #include "base/macros.h" +#include "ui/aura/client/focus_change_observer.h" +#include "ui/aura/window_observer.h" #include "ui/views/views_export.h" namespace base { @@ -18,6 +20,9 @@ template <typename T> struct DefaultSingletonTraits; } namespace aura { +namespace client { +class FocusClient; +} class Window; } // namespace aura @@ -27,7 +32,9 @@ class View; class Widget; // A cache responsible for assigning id's to a set of interesting Aura views. -class VIEWS_EXPORT AXAuraObjCache { +class VIEWS_EXPORT AXAuraObjCache + : public aura::client::FocusChangeObserver, + public aura::WindowObserver { public: // Get the single instance of this class. static AXAuraObjCache* GetInstance(); @@ -38,9 +45,9 @@ class VIEWS_EXPORT AXAuraObjCache { AXAuraObjWrapper* GetOrCreate(aura::Window* window); // Gets an id given an Aura view. - int32_t GetID(View* view); - int32_t GetID(Widget* widget); - int32_t GetID(aura::Window* window); + int32_t GetID(View* view) const; + int32_t GetID(Widget* widget) const; + int32_t GetID(aura::Window* window) const; // Gets the next unique id for this cache. Useful for non-Aura view backed // views. @@ -63,6 +70,9 @@ class VIEWS_EXPORT AXAuraObjCache { // Get all top level windows this cache knows about. void GetTopLevelWindows(std::vector<AXAuraObjWrapper*>* children); + // Get the object that has focus. + AXAuraObjWrapper* GetFocus(); + // Indicates if this object's currently being destroyed. bool is_destroying() { return is_destroying_; } @@ -70,7 +80,16 @@ class VIEWS_EXPORT AXAuraObjCache { friend struct base::DefaultSingletonTraits<AXAuraObjCache>; AXAuraObjCache(); - virtual ~AXAuraObjCache(); + ~AXAuraObjCache() override; + + View* GetFocusedView(); + + // aura::client::FocusChangeObserver override. + void OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) override; + + // aura::WindowObserver override. + void OnWindowDestroying(aura::Window* window) override; template <typename AuraViewWrapper, typename AuraView> AXAuraObjWrapper* CreateInternal( @@ -78,8 +97,9 @@ class VIEWS_EXPORT AXAuraObjCache { std::map<AuraView*, int32_t>& aura_view_to_id_map); template <typename AuraView> - int32_t GetIDInternal(AuraView* aura_view, - std::map<AuraView*, int32_t>& aura_view_to_id_map); + int32_t GetIDInternal( + AuraView* aura_view, + const std::map<AuraView*, int32_t>& aura_view_to_id_map) const; template <typename AuraView> void RemoveInternal(AuraView* aura_view, @@ -92,6 +112,8 @@ class VIEWS_EXPORT AXAuraObjCache { std::map<int32_t, AXAuraObjWrapper*> cache_; int32_t current_id_; + aura::client::FocusClient* focus_client_; + // True immediately when entering this object's destructor. bool is_destroying_; diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache_unittest.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache_unittest.cc new file mode 100644 index 00000000000..80122369c31 --- /dev/null +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache_unittest.cc @@ -0,0 +1,61 @@ +// 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/views/accessibility/ax_aura_obj_cache.h" +#include "ui/views/test/widget_test.h" + +namespace views { +namespace test { + +namespace { + +// This class can be used as a deleter for scoped_ptr<Widget> +// to call function Widget::CloseNow automatically. +struct WidgetCloser { + inline void operator()(Widget* widget) const { widget->CloseNow(); } +}; + +using WidgetAutoclosePtr = scoped_ptr<Widget, WidgetCloser>; +} + +class AXAuraObjCacheTest : public WidgetTest { + public: + AXAuraObjCacheTest() {} + ~AXAuraObjCacheTest() override {} +}; + +TEST_F(AXAuraObjCacheTest, TestViewRemoval) { + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); + View* parent = new View(); + widget->GetRootView()->AddChildView(parent); + View* child = new View(); + parent->AddChildView(child); + + AXAuraObjCache* cache = AXAuraObjCache::GetInstance(); + AXAuraObjWrapper* ax_widget = cache->GetOrCreate(widget.get()); + ASSERT_NE(nullptr, ax_widget); + AXAuraObjWrapper* ax_parent = cache->GetOrCreate(parent); + ASSERT_NE(nullptr, ax_parent); + AXAuraObjWrapper* ax_child = cache->GetOrCreate(child); + ASSERT_NE(nullptr, ax_child); + + // Everything should have an ID, indicating it's in the cache. + ASSERT_GT(cache->GetID(widget.get()), 0); + ASSERT_GT(cache->GetID(parent), 0); + ASSERT_GT(cache->GetID(child), 0); + + // Removing the parent view should remove both the parent and child + // from the cache, but leave the widget. + widget->GetRootView()->RemoveChildView(parent); + ASSERT_GT(cache->GetID(widget.get()), 0); + ASSERT_EQ(-1, cache->GetID(parent)); + ASSERT_EQ(-1, cache->GetID(child)); + + // Explicitly delete |parent| to prevent a memory leak, since calling + // RemoveChildView() doesn't delete it. + delete parent; +} + +} // namespace test +} // namespace views diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc index 4363d19dc71..3ea1e04efb2 100644 --- a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc @@ -36,6 +36,9 @@ void AXViewObjWrapper::GetChildren( std::vector<AXAuraObjWrapper*>* out_children) { // TODO(dtseng): Need to handle |Widget| child of |View|. for (int i = 0; i < view_->child_count(); ++i) { + if (!view_->child_at(i)->visible()) + continue; + AXAuraObjWrapper* child = AXAuraObjCache::GetInstance()->GetOrCreate(view_->child_at(i)); out_children->push_back(child); @@ -50,8 +53,6 @@ void AXViewObjWrapper::Serialize(ui::AXNodeData* out_node_data) { out_node_data->role = view_data.role; out_node_data->state = view_data.state(); - if (view_->HasFocus()) - out_node_data->state |= 1 << ui::AX_STATE_FOCUSED; if (view_->IsFocusable()) out_node_data->state |= 1 << ui::AX_STATE_FOCUSABLE; if (!view_->visible()) diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc index 25183a8c22f..628b82fac76 100644 --- a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc @@ -30,6 +30,9 @@ AXAuraObjWrapper* AXWidgetObjWrapper::GetParent() { void AXWidgetObjWrapper::GetChildren( std::vector<AXAuraObjWrapper*>* out_children) { + if (!widget_->IsVisible() || !widget_->GetRootView()->visible()) + return; + out_children->push_back( AXAuraObjCache::GetInstance()->GetOrCreate(widget_->GetRootView())); } @@ -51,7 +54,7 @@ void AXWidgetObjWrapper::OnWidgetDestroying(Widget* widget) { } void AXWidgetObjWrapper::OnWillRemoveView(Widget* widget, View* view) { - AXAuraObjCache::GetInstance()->Remove(view); + AXAuraObjCache::GetInstance()->RemoveViewSubtree(view); } } // namespace views diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc index 124ae78710d..ebdb650d1a6 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc @@ -35,6 +35,8 @@ void AXWindowObjWrapper::GetChildren( std::vector<AXAuraObjWrapper*>* out_children) { aura::Window::Windows children = window_->children(); for (size_t i = 0; i < children.size(); ++i) { + if (!children[i]->IsVisible()) + continue; out_children->push_back( AXAuraObjCache::GetInstance()->GetOrCreate(children[i])); } @@ -64,7 +66,7 @@ int32_t AXWindowObjWrapper::GetID() { return AXAuraObjCache::GetInstance()->GetID(window_); } -void AXWindowObjWrapper::OnWindowDestroying(aura::Window* window) { +void AXWindowObjWrapper::OnWindowDestroyed(aura::Window* window) { AXAuraObjCache::GetInstance()->Remove(window); } diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h index 2bc46aaa9cf..bcf35481d73 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h @@ -37,7 +37,7 @@ class AXWindowObjWrapper : public AXAuraObjWrapper, int32_t GetID() override; // WindowObserver overrides. - void OnWindowDestroying(aura::Window* window) override; + void OnWindowDestroyed(aura::Window* window) override; private: aura::Window* window_; diff --git a/chromium/ui/views/accessibility/native_view_accessibility.cc b/chromium/ui/views/accessibility/native_view_accessibility.cc index 07406e5b2ed..ab86be128e3 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility.cc @@ -74,9 +74,6 @@ const ui::AXNodeData& NativeViewAccessibility::GetData() { if (!view_->visible()) data_.state |= (1 << ui::AX_STATE_INVISIBLE); - if (view_->HasFocus()) - data_.state |= (1 << ui::AX_STATE_FOCUSED); - return data_; } diff --git a/chromium/ui/views/accessible_pane_view_unittest.cc b/chromium/ui/views/accessible_pane_view_unittest.cc index 303d20623da..864481ff3ff 100644 --- a/chromium/ui/views/accessible_pane_view_unittest.cc +++ b/chromium/ui/views/accessible_pane_view_unittest.cc @@ -12,10 +12,6 @@ #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" -#if defined(OS_MACOSX) -#include "ui/base/test/scoped_fake_nswindow_focus.h" -#endif - namespace views { // TODO(alicet): bring pane rotation into views and add tests. @@ -105,13 +101,6 @@ TEST_F(AccessiblePaneViewTest, SimpleSetPaneFocus) { } TEST_F(AccessiblePaneViewTest, SetPaneFocusAndRestore) { -#if defined(OS_MACOSX) - // On Aura platforms, this test creates Ash windows and only interacts with - // the Ash window manager. On Mac, it creates native windows, but since unit - // tests cannot gain key status, fake it out here. - ui::test::ScopedFakeNSWindowFocus fake_focus; -#endif - View* test_view_main = new View(); scoped_ptr<Widget> widget_main(new Widget()); Widget::InitParams params_main = CreateParams(Widget::InitParams::TYPE_POPUP); diff --git a/chromium/ui/views/animation/OWNERS b/chromium/ui/views/animation/OWNERS new file mode 100644 index 00000000000..9710b4e0f93 --- /dev/null +++ b/chromium/ui/views/animation/OWNERS @@ -0,0 +1 @@ +per-file *ink*=bruthig@chromium.org diff --git a/chromium/ui/views/animation/bounds_animator.h b/chromium/ui/views/animation/bounds_animator.h index 68dad46abc6..2840c3dcd73 100644 --- a/chromium/ui/views/animation/bounds_animator.h +++ b/chromium/ui/views/animation/bounds_animator.h @@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "ui/gfx/animation/animation_container_observer.h" #include "ui/gfx/animation/animation_delegate.h" diff --git a/chromium/ui/views/animation/button_ink_drop_delegate.cc b/chromium/ui/views/animation/button_ink_drop_delegate.cc index 2fa6000574b..73152d77bc5 100644 --- a/chromium/ui/views/animation/button_ink_drop_delegate.cc +++ b/chromium/ui/views/animation/button_ink_drop_delegate.cc @@ -25,30 +25,37 @@ ButtonInkDropDelegate::ButtonInkDropDelegate(InkDropHost* ink_drop_host, ButtonInkDropDelegate::~ButtonInkDropDelegate() { } -void ButtonInkDropDelegate::SetInkDropSize(int large_size, - int large_corner_radius, - int small_size, - int small_corner_radius) { - ink_drop_animation_controller_->SetInkDropSize( - gfx::Size(large_size, large_size), large_corner_radius, - gfx::Size(small_size, small_size), small_corner_radius); +void ButtonInkDropDelegate::OnAction(InkDropState state) { + ink_drop_animation_controller_->AnimateToState(state); } -void ButtonInkDropDelegate::OnLayout() { - ink_drop_animation_controller_->SetInkDropCenter( - ink_drop_host_->CalculateInkDropCenter()); +void ButtonInkDropDelegate::SnapToActivated() { + ink_drop_animation_controller_->SnapToActivated(); } -void ButtonInkDropDelegate::OnAction(InkDropState state) { - ink_drop_animation_controller_->AnimateToState(state); +void ButtonInkDropDelegate::SetHovered(bool is_hovered) { + ink_drop_animation_controller_->SetHovered(is_hovered); } //////////////////////////////////////////////////////////////////////////////// // ui::EventHandler: +void ButtonInkDropDelegate::OnMouseEvent(ui::MouseEvent* event) { + switch (event->type()) { + case ui::ET_MOUSE_ENTERED: + SetHovered(true); + break; + case ui::ET_MOUSE_EXITED: + SetHovered(false); + break; + default: + return; + } +} + void ButtonInkDropDelegate::OnGestureEvent(ui::GestureEvent* event) { InkDropState current_ink_drop_state = - ink_drop_animation_controller_->GetInkDropState(); + ink_drop_animation_controller_->GetTargetInkDropState(); InkDropState ink_drop_state = InkDropState::HIDDEN; switch (event->type()) { @@ -59,10 +66,10 @@ void ButtonInkDropDelegate::OnGestureEvent(ui::GestureEvent* event) { event->SetHandled(); break; case ui::ET_GESTURE_LONG_PRESS: - ink_drop_state = InkDropState::SLOW_ACTION_PENDING; + ink_drop_state = InkDropState::ALTERNATE_ACTION_PENDING; break; case ui::ET_GESTURE_LONG_TAP: - ink_drop_state = InkDropState::SLOW_ACTION; + ink_drop_state = InkDropState::ALTERNATE_ACTION_TRIGGERED; break; case ui::ET_GESTURE_END: if (current_ink_drop_state == InkDropState::ACTIVATED) @@ -75,9 +82,11 @@ void ButtonInkDropDelegate::OnGestureEvent(ui::GestureEvent* event) { return; } + last_ink_drop_location_ = event->location(); + if (ink_drop_state == InkDropState::HIDDEN && - (current_ink_drop_state == InkDropState::QUICK_ACTION || - current_ink_drop_state == InkDropState::SLOW_ACTION || + (current_ink_drop_state == InkDropState::ACTION_TRIGGERED || + current_ink_drop_state == InkDropState::ALTERNATE_ACTION_TRIGGERED || current_ink_drop_state == InkDropState::DEACTIVATED)) { // These InkDropStates automatically transition to the HIDDEN state so we // don't make an explicit call. Explicitly animating to HIDDEN in this case diff --git a/chromium/ui/views/animation/button_ink_drop_delegate.h b/chromium/ui/views/animation/button_ink_drop_delegate.h index 2e8798daa5a..f4c245ecdc1 100644 --- a/chromium/ui/views/animation/button_ink_drop_delegate.h +++ b/chromium/ui/views/animation/button_ink_drop_delegate.h @@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "ui/events/event_handler.h" +#include "ui/gfx/geometry/point.h" #include "ui/views/animation/ink_drop_delegate.h" #include "ui/views/views_export.h" @@ -21,22 +22,27 @@ class InkDropAnimationController; class InkDropHost; class View; -// An InkDropDelegate that handles animations for toolbar buttons. +// An InkDropDelegate that handles animations for things that act like buttons. class VIEWS_EXPORT ButtonInkDropDelegate : public InkDropDelegate, public ui::EventHandler { public: ButtonInkDropDelegate(InkDropHost* ink_drop_host, View* view); ~ButtonInkDropDelegate() override; + const gfx::Point& last_ink_drop_location() const { + return last_ink_drop_location_; + } + void set_last_ink_drop_location(const gfx::Point& point) { + last_ink_drop_location_ = point; + } + // InkDropDelegate: - void SetInkDropSize(int large_size, - int large_corner_radius, - int small_size, - int small_corner_radius) override; - void OnLayout() override; void OnAction(InkDropState state) override; + void SnapToActivated() override; + void SetHovered(bool is_hovered) override; // ui::EventHandler: + void OnMouseEvent(ui::MouseEvent* event) override; void OnGestureEvent(ui::GestureEvent* event) override; private: @@ -46,6 +52,10 @@ class VIEWS_EXPORT ButtonInkDropDelegate : public InkDropDelegate, // Parent InkDropHost (typically a View) that hosts the ink ripple animations. InkDropHost* ink_drop_host_; + // Location of the last ink drop triggering event in coordinate system of the + // ctor argument |view|. + gfx::Point last_ink_drop_location_; + // Animation controller for the ink drop ripple effect. scoped_ptr<InkDropAnimationController> ink_drop_animation_controller_; diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_animation.cc b/chromium/ui/views/animation/flood_fill_ink_drop_animation.cc new file mode 100644 index 00000000000..6e177aaff37 --- /dev/null +++ b/chromium/ui/views/animation/flood_fill_ink_drop_animation.cc @@ -0,0 +1,327 @@ +// 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/views/animation/flood_fill_ink_drop_animation.h" + +#include <algorithm> + +#include "base/logging.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animation_sequence.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace { + +// The minimum radius to use when scaling the painted layers. Smaller values +// were causing visual anomalies. +const float kMinRadius = 1.f; + +// All the sub animations that are used to animate each of the InkDropStates. +// These are used to get time durations with +// GetAnimationDuration(InkDropSubAnimations). Note that in general a sub +// animation defines the duration for either a transformation animation or an +// opacity animation but there are some exceptions where an entire InkDropState +// animation consists of only 1 sub animation and it defines the duration for +// both the transformation and opacity animations. +enum InkDropSubAnimations { + // HIDDEN sub animations. + + // The HIDDEN sub animation that is fading out to a hidden opacity. + HIDDEN_FADE_OUT, + + // The HIDDEN sub animation that transform the circle to a small one. + HIDDEN_TRANSFORM, + + // ACTION_PENDING sub animations. + + // The ACTION_PENDING sub animation that fades in to the visible opacity. + ACTION_PENDING_FADE_IN, + + // The ACTION_PENDING sub animation that transforms the circle to fill the + // bounds. + ACTION_PENDING_TRANSFORM, + + // ACTION_TRIGGERED sub animations. + + // The ACTION_TRIGGERED sub animation that is fading out to a hidden opacity. + ACTION_TRIGGERED_FADE_OUT, + + // ALTERNATE_ACTION_PENDING sub animations. + + // The ALTERNATE_ACTION_PENDING animation has only one sub animation which + // animates + // the circleto fill the bounds at visible opacity. + ALTERNATE_ACTION_PENDING, + + // ALTERNATE_ACTION_TRIGGERED sub animations. + + // The ALTERNATE_ACTION_TRIGGERED sub animation that is fading out to a hidden + // opacity. + ALTERNATE_ACTION_TRIGGERED_FADE_OUT, + + // ACTIVATED sub animations. + + // The ACTIVATED sub animation that is fading in to the visible opacity. + ACTIVATED_FADE_IN, + + // The ACTIVATED sub animation that transforms the circle to fill the entire + // bounds. + ACTIVATED_TRANSFORM, + + // DEACTIVATED sub animations. + + // The DEACTIVATED sub animation that is fading out to a hidden opacity. + DEACTIVATED_FADE_OUT, +}; + +// Duration constants for InkDropStateSubAnimations. See the +// InkDropStateSubAnimations enum documentation for more info. +int kAnimationDurationInMs[] = { + 200, // HIDDEN_FADE_OUT + 300, // HIDDEN_TRANSFORM + 0, // ACTION_PENDING_FADE_IN + 240, // ACTION_PENDING_TRANSFORM + 300, // ACTION_TRIGGERED_FADE_OUT + 200, // ALTERNATE_ACTION_PENDING + 300, // ALTERNATE_ACTION_TRIGGERED_FADE_OUT + 150, // ACTIVATED_FADE_IN + 200, // ACTIVATED_TRANSFORM + 300, // DEACTIVATED_FADE_OUT +}; + +// Returns the InkDropState sub animation duration for the given |state|. +base::TimeDelta GetAnimationDuration(InkDropSubAnimations state) { + return base::TimeDelta::FromMilliseconds( + (views::InkDropAnimation::UseFastAnimations() + ? 1 + : views::InkDropAnimation::kSlowAnimationDurationFactor) * + kAnimationDurationInMs[state]); +} + +} // namespace + +namespace views { + +FloodFillInkDropAnimation::FloodFillInkDropAnimation( + const gfx::Rect& clip_bounds, + const gfx::Point& center_point, + SkColor color) + : clip_bounds_(clip_bounds), + center_point_(center_point), + root_layer_(ui::LAYER_NOT_DRAWN), + circle_layer_delegate_( + color, + std::max(clip_bounds_.width(), clip_bounds_.height()) / 2.f), + ink_drop_state_(InkDropState::HIDDEN) { + root_layer_.set_name("FloodFillInkDropAnimation:ROOT_LAYER"); + root_layer_.SetMasksToBounds(true); + root_layer_.SetBounds(clip_bounds); + + const int painted_size_length = + 2 * std::max(clip_bounds_.width(), clip_bounds_.height()); + + painted_layer_.SetBounds(gfx::Rect(painted_size_length, painted_size_length)); + painted_layer_.SetFillsBoundsOpaquely(false); + painted_layer_.set_delegate(&circle_layer_delegate_); + painted_layer_.SetVisible(true); + painted_layer_.SetOpacity(1.0); + painted_layer_.SetMasksToBounds(false); + painted_layer_.set_name("FloodFillInkDropAnimation:PAINTED_LAYER"); + + root_layer_.Add(&painted_layer_); + + SetStateToHidden(); +} + +FloodFillInkDropAnimation::~FloodFillInkDropAnimation() { + // Explicitly aborting all the animations ensures all callbacks are invoked + // while this instance still exists. + AbortAllAnimations(); +} + +void FloodFillInkDropAnimation::SnapToActivated() { + InkDropAnimation::SnapToActivated(); + SetOpacity(kVisibleOpacity); + painted_layer_.SetTransform(GetMaxSizeTargetTransform()); +} + +ui::Layer* FloodFillInkDropAnimation::GetRootLayer() { + return &root_layer_; +} + +bool FloodFillInkDropAnimation::IsVisible() const { + return root_layer_.visible(); +} + +void FloodFillInkDropAnimation::AnimateStateChange( + InkDropState old_ink_drop_state, + InkDropState new_ink_drop_state, + ui::LayerAnimationObserver* animation_observer) { + switch (new_ink_drop_state) { + case InkDropState::HIDDEN: + if (!IsVisible()) { + SetStateToHidden(); + } else { + AnimateToOpacity(kHiddenOpacity, GetAnimationDuration(HIDDEN_FADE_OUT), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + const gfx::Transform transform = CalculateTransform(kMinRadius); + AnimateToTransform(transform, GetAnimationDuration(HIDDEN_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + } + break; + case InkDropState::ACTION_PENDING: { + DCHECK(old_ink_drop_state == InkDropState::HIDDEN); + + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ACTION_PENDING_FADE_IN), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ACTION_PENDING_TRANSFORM) - + GetAnimationDuration(ACTION_PENDING_FADE_IN), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN, animation_observer); + + AnimateToTransform(GetMaxSizeTargetTransform(), + GetAnimationDuration(ACTION_PENDING_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::FAST_OUT_SLOW_IN, animation_observer); + break; + } + case InkDropState::ACTION_TRIGGERED: { + DCHECK(old_ink_drop_state == InkDropState::HIDDEN || + old_ink_drop_state == InkDropState::ACTION_PENDING); + if (old_ink_drop_state == InkDropState::HIDDEN) { + AnimateStateChange(old_ink_drop_state, InkDropState::ACTION_PENDING, + animation_observer); + } + AnimateToOpacity(kHiddenOpacity, + GetAnimationDuration(ACTION_TRIGGERED_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::ALTERNATE_ACTION_PENDING: { + DCHECK(old_ink_drop_state == InkDropState::ACTION_PENDING); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ALTERNATE_ACTION_PENDING), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + AnimateToTransform(GetMaxSizeTargetTransform(), + GetAnimationDuration(ALTERNATE_ACTION_PENDING), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + DCHECK(old_ink_drop_state == InkDropState::ALTERNATE_ACTION_PENDING); + AnimateToOpacity(kHiddenOpacity, GetAnimationDuration( + ALTERNATE_ACTION_TRIGGERED_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + case InkDropState::ACTIVATED: { + AnimateToOpacity(kVisibleOpacity, GetAnimationDuration(ACTIVATED_FADE_IN), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + AnimateToTransform(GetMaxSizeTargetTransform(), + GetAnimationDuration(ACTIVATED_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::DEACTIVATED: + AnimateToOpacity(kHiddenOpacity, + GetAnimationDuration(DEACTIVATED_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } +} + +void FloodFillInkDropAnimation::SetStateToHidden() { + painted_layer_.SetTransform(CalculateTransform(kMinRadius)); + root_layer_.SetOpacity(InkDropAnimation::kHiddenOpacity); + root_layer_.SetVisible(false); +} + +void FloodFillInkDropAnimation::AbortAllAnimations() { + root_layer_.GetAnimator()->AbortAllAnimations(); + painted_layer_.GetAnimator()->AbortAllAnimations(); +} + +void FloodFillInkDropAnimation::AnimateToTransform( + const gfx::Transform& transform, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* animation_observer) { + ui::LayerAnimator* animator = painted_layer_.GetAnimator(); + ui::ScopedLayerAnimationSettings animation(animator); + animation.SetPreemptionStrategy(preemption_strategy); + animation.SetTweenType(tween); + ui::LayerAnimationElement* element = + ui::LayerAnimationElement::CreateTransformElement(transform, duration); + ui::LayerAnimationSequence* sequence = + new ui::LayerAnimationSequence(element); + + if (animation_observer) + sequence->AddObserver(animation_observer); + + animator->StartAnimation(sequence); +} + +void FloodFillInkDropAnimation::SetOpacity(float opacity) { + root_layer_.SetOpacity(opacity); +} + +void FloodFillInkDropAnimation::AnimateToOpacity( + float opacity, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* animation_observer) { + ui::LayerAnimator* animator = root_layer_.GetAnimator(); + ui::ScopedLayerAnimationSettings animation_settings(animator); + animation_settings.SetPreemptionStrategy(preemption_strategy); + animation_settings.SetTweenType(tween); + ui::LayerAnimationElement* animation_element = + ui::LayerAnimationElement::CreateOpacityElement(opacity, duration); + ui::LayerAnimationSequence* animation_sequence = + new ui::LayerAnimationSequence(animation_element); + + if (animation_observer) + animation_sequence->AddObserver(animation_observer); + + animator->StartAnimation(animation_sequence); +} + +gfx::Transform FloodFillInkDropAnimation::CalculateTransform( + float target_radius) const { + const float target_scale = target_radius / circle_layer_delegate_.radius(); + const gfx::Point drawn_center_point = + ToRoundedPoint(circle_layer_delegate_.GetCenterPoint()); + + gfx::Transform transform = gfx::Transform(); + transform.Translate(center_point_.x(), center_point_.y()); + transform.Scale(target_scale, target_scale); + transform.Translate(-drawn_center_point.x() - root_layer_.bounds().x(), + -drawn_center_point.y() - root_layer_.bounds().y()); + + return transform; +} + +gfx::Transform FloodFillInkDropAnimation::GetMaxSizeTargetTransform() const { + // TODO(estade): get rid of this 2, but make the fade out start before the + // active/action transform is done. + return CalculateTransform( + gfx::Vector2dF(clip_bounds_.width(), clip_bounds_.height()).Length() / 2); +} + +} // namespace views diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_animation.h b/chromium/ui/views/animation/flood_fill_ink_drop_animation.h new file mode 100644 index 00000000000..2ddde9647af --- /dev/null +++ b/chromium/ui/views/animation/flood_fill_ink_drop_animation.h @@ -0,0 +1,131 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_FLOOD_FILL_INK_DROP_ANIMATION_H_ +#define UI_VIEWS_ANIMATION_FLOOD_FILL_INK_DROP_ANIMATION_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animator.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/transform.h" +#include "ui/views/animation/ink_drop_animation.h" +#include "ui/views/animation/ink_drop_painted_layer_delegates.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/views_export.h" + +namespace ui { +class Layer; +} // namespace ui + +namespace views { +class CircleLayerDelegate; + +namespace test { +class FloodFillInkDropAnimationTestApi; +} // namespace test + +// An ink drop animation that starts as a small circle and flood fills a +// rectangle of the given size. The circle is clipped to the rectangles bounds. +// +// The valid InkDropState transitions are defined below: +// +// {All InkDropStates} => HIDDEN +// HIDDEN => ACTION_PENDING +// HIDDEN, ACTION_PENDING => ACTION_TRIGGERED +// ACTION_PENDING => ALTERNATE_ACTION_PENDING +// ALTERNATE_ACTION_PENDING => ALTERNATE_ACTION_TRIGGERED +// {All InkDropStates} => ACTIVATED +// {All InkDropStates} => DEACTIVATED +// +class VIEWS_EXPORT FloodFillInkDropAnimation : public InkDropAnimation { + public: + FloodFillInkDropAnimation(const gfx::Rect& clip_bounds, + const gfx::Point& center_point, + SkColor color); + ~FloodFillInkDropAnimation() override; + + // InkDropAnimation: + void SnapToActivated() override; + ui::Layer* GetRootLayer() override; + bool IsVisible() const override; + + private: + friend class test::FloodFillInkDropAnimationTestApi; + + // InkDropAnimation: + void AnimateStateChange(InkDropState old_ink_drop_state, + InkDropState new_ink_drop_state, + ui::LayerAnimationObserver* observer) override; + void SetStateToHidden() override; + void AbortAllAnimations() override; + + // Animates the |painted_layer_| to the specified |transform|. The animation + // will be configured with the given |duration|, |tween|, and + // |preemption_strategy| values. The |observer| will be added to all + // LayerAnimationSequences if not null. + void AnimateToTransform( + const gfx::Transform& transform, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* observer); + + // Sets the opacity of the ink drop. Note that this does not perform any + // animation. + void SetOpacity(float opacity); + + // Animates the |painted_layer_| to the specified |opacity|. The animation + // will be configured with the given |duration|, |tween|, and + // |preemption_strategy| values. The |observer| will be added to all + // LayerAnimationSequences if not null. + void AnimateToOpacity( + float opacity, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* observer); + + // Returns the Transform to be applied to the |painted_layer_| for the given + // |target_radius|. + gfx::Transform CalculateTransform(float target_radius) const; + + // Returns the target Transform for when the ink drop is fully shown. + gfx::Transform GetMaxSizeTargetTransform() const; + + // The clip bounds. + const gfx::Rect clip_bounds_; + + // The point where the Center of the ink drop's circle should be drawn. + gfx::Point center_point_; + + // The root layer that parents the animating layer. The root layer is used to + // manipulate opacity and clipping bounds, and it child is used to manipulate + // the different shape of the ink drop. + ui::Layer root_layer_; + + // ui::LayerDelegate to paint the |painted_layer_|. + CircleLayerDelegate circle_layer_delegate_; + + // Child ui::Layer of |root_layer_|. Used to manipulate the different size + // and shape of the ink drop. + ui::Layer painted_layer_; + + // The current ink drop state. + InkDropState ink_drop_state_; + + DISALLOW_COPY_AND_ASSIGN(FloodFillInkDropAnimation); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_FLOOD_FILL_INK_DROP_ANIMATION_H_ diff --git a/chromium/ui/views/animation/ink_drop_animation.cc b/chromium/ui/views/animation/ink_drop_animation.cc index cf0c705e7ba..214716da23a 100644 --- a/chromium/ui/views/animation/ink_drop_animation.cc +++ b/chromium/ui/views/animation/ink_drop_animation.cc @@ -4,56 +4,18 @@ #include "ui/views/animation/ink_drop_animation.h" -#include <algorithm> - +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/command_line.h" -#include "base/logging.h" -#include "third_party/skia/include/core/SkColor.h" #include "ui/base/ui_base_switches.h" #include "ui/compositor/callback_layer_animation_observer.h" #include "ui/compositor/layer.h" -#include "ui/compositor/layer_animation_sequence.h" -#include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/transform_util.h" -#include "ui/views/animation/ink_drop_animation_observer.h" -#include "ui/views/animation/ink_drop_painted_layer_delegates.h" -#include "ui/views/view.h" - -namespace { - -// The minimum scale factor to use when scaling rectangle layers. Smaller values -// were causing visual anomalies. -const float kMinimumRectScale = 0.0001f; - -// The minimum scale factor to use when scaling circle layers. Smaller values -// were causing visual anomalies. -const float kMinimumCircleScale = 0.001f; - -// The ink drop color. -const SkColor kInkDropColor = SK_ColorBLACK; - -// The opacity of the ink drop when it is visible. -const float kVisibleOpacity = 0.14f; -// The opacity of the ink drop when it is not visible. -const float kHiddenOpacity = 0.0f; - -// Durations for the different InkDropState animations in milliseconds. -const int kHiddenStateAnimationDurationMs = 1; -const int kActionPendingStateAnimationDurationMs = 500; -const int kQuickActionStateAnimationDurationMs = 250; -const int kSlowActionPendingStateAnimationDurationMs = 500; -const int kSlowActionStateAnimationDurationMs = 250; -const int kActivatedStateAnimationDurationMs = 125; -const int kDeactivatedStateAnimationDurationMs = 250; +namespace views { -// A multiplicative factor used to slow down InkDropState animations. -const int kSlowAnimationDurationFactor = 3; +const double InkDropAnimation::kSlowAnimationDurationFactor = 3.0; -// Checks CommandLine switches to determine if the visual feedback should have -// a fast animations speed. -bool UseFastAnimations() { +bool InkDropAnimation::UseFastAnimations() { static bool fast = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( (::switches::kMaterialDesignInkDropAnimationSpeed)) != @@ -61,391 +23,94 @@ bool UseFastAnimations() { return fast; } -// Returns the InkDropState animation duration for the given |state|. -base::TimeDelta GetAnimationDuration(views::InkDropState state) { - int duration = 0; - switch (state) { - case views::InkDropState::HIDDEN: - duration = kHiddenStateAnimationDurationMs; - break; - case views::InkDropState::ACTION_PENDING: - duration = kActionPendingStateAnimationDurationMs; - break; - case views::InkDropState::QUICK_ACTION: - duration = kQuickActionStateAnimationDurationMs; - break; - case views::InkDropState::SLOW_ACTION_PENDING: - duration = kSlowActionPendingStateAnimationDurationMs; - break; - case views::InkDropState::SLOW_ACTION: - duration = kSlowActionStateAnimationDurationMs; - break; - case views::InkDropState::ACTIVATED: - duration = kActivatedStateAnimationDurationMs; - break; - case views::InkDropState::DEACTIVATED: - duration = kDeactivatedStateAnimationDurationMs; - break; - } - - return base::TimeDelta::FromMilliseconds( - (UseFastAnimations() ? 1 : kSlowAnimationDurationFactor) * duration); -} - -// Calculates a Transform for a circle layer. The transform will be set up to -// translate the |drawn_center_point| to the origin, scale, and then translate -// to the target point defined by |target_center_x| and |target_center_y|. -gfx::Transform CalculateCircleTransform(const gfx::Point& drawn_center_point, - float scale, - float target_center_x, - float target_center_y) { - gfx::Transform transform; - transform.Translate(target_center_x, target_center_y); - transform.Scale(scale, scale); - transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); - return transform; -} - -// Calculates a Transform for a rectangle layer. The transform will be set up to -// translate the |drawn_center_point| to the origin and then scale by the -// |x_scale| and |y_scale| factors. -gfx::Transform CalculateRectTransform(const gfx::Point& drawn_center_point, - float x_scale, - float y_scale) { - gfx::Transform transform; - transform.Scale(x_scale, y_scale); - transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); - return transform; -} - -} // namespace +const float InkDropAnimation::kHiddenOpacity = 0.f; +const float InkDropAnimation::kVisibleOpacity = 0.175f; -namespace views { +InkDropAnimation::InkDropAnimation() + : target_ink_drop_state_(InkDropState::HIDDEN), observer_(nullptr) {} -InkDropAnimation::InkDropAnimation(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius) - : large_size_(large_size), - large_corner_radius_(large_corner_radius), - small_size_(small_size), - small_corner_radius_(small_corner_radius), - circle_layer_delegate_(new CircleLayerDelegate( - kInkDropColor, - std::min(large_size_.width(), large_size_.height()) / 2)), - rect_layer_delegate_( - new RectangleLayerDelegate(kInkDropColor, large_size_)), - root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)), - ink_drop_state_(InkDropState::HIDDEN) { - root_layer_->set_name("InkDropAnimation:ROOT_LAYER"); - - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - AddPaintLayer(static_cast<PaintedShape>(i)); - - root_layer_->SetMasksToBounds(false); - root_layer_->SetBounds(gfx::Rect(large_size_)); - - SetStateToHidden(); -} - -InkDropAnimation::~InkDropAnimation() { - // Explicitly aborting all the animations ensures all callbacks are invoked - // while this instance still exists. - AbortAllAnimations(); -} - -void InkDropAnimation::AddObserver(InkDropAnimationObserver* observer) { - observers_.AddObserver(observer); -} - -void InkDropAnimation::RemoveObserver(InkDropAnimationObserver* observer) { - observers_.RemoveObserver(observer); -} +InkDropAnimation::~InkDropAnimation() {} void InkDropAnimation::AnimateToState(InkDropState ink_drop_state) { + // Does not return early if |target_ink_drop_state_| == |ink_drop_state| for + // two reasons. + // 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(). + // |animation_observer| will be deleted when AnimationEndedCallback() returns // true. + // TODO(bruthig): Implement a safer ownership model for the + // |animation_observer|. ui::CallbackLayerAnimationObserver* animation_observer = new ui::CallbackLayerAnimationObserver( base::Bind(&InkDropAnimation::AnimationStartedCallback, base::Unretained(this), ink_drop_state), base::Bind(&InkDropAnimation::AnimationEndedCallback, base::Unretained(this), ink_drop_state)); - AnimateToStateInternal(ink_drop_state, animation_observer); - animation_observer->SetActive(); -} -void InkDropAnimation::SetCenterPoint(const gfx::Point& center_point) { - gfx::Transform transform; - transform.Translate(center_point.x(), center_point.y()); - root_layer_->SetTransform(transform); -} + InkDropState old_ink_drop_state = target_ink_drop_state_; + // Assign to |target_ink_drop_state_| before calling AnimateStateChange() so + // that any observers notified as a side effect of the AnimateStateChange() + // will get the target InkDropState when calling GetInkDropState(). + target_ink_drop_state_ = ink_drop_state; -std::string InkDropAnimation::ToLayerName(PaintedShape painted_shape) { - switch (painted_shape) { - case TOP_LEFT_CIRCLE: - return "TOP_LEFT_CIRCLE"; - case TOP_RIGHT_CIRCLE: - return "TOP_RIGHT_CIRCLE"; - case BOTTOM_RIGHT_CIRCLE: - return "BOTTOM_RIGHT_CIRCLE"; - case BOTTOM_LEFT_CIRCLE: - return "BOTTOM_LEFT_CIRCLE"; - case HORIZONTAL_RECT: - return "HORIZONTAL_RECT"; - case VERTICAL_RECT: - return "VERTICAL_RECT"; - case PAINTED_SHAPE_COUNT: - NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used."; - return "PAINTED_SHAPE_COUNT"; + if (old_ink_drop_state == InkDropState::HIDDEN && + target_ink_drop_state_ != InkDropState::HIDDEN) { + GetRootLayer()->SetVisible(true); } - return "UNKNOWN"; -} - -void InkDropAnimation::AnimateToStateInternal( - InkDropState ink_drop_state, - ui::LayerAnimationObserver* animation_observer) { - ink_drop_state_ = ink_drop_state; - - if (ink_drop_state_ == InkDropState::HIDDEN) { - // Animating to the HIDDEN state doesn't actually use any - // LayerAnimationSequences so we need to explicitly abort any running ones - // so that observers receive an InkDropAnimationEnded() event for the - // running animation prior to receiving an InkDropAnimationStarted() event - // for the HIDDEN 'animation'. - AbortAllAnimations(); - root_layer_->SetVisible(false); - SetStateToHidden(); - return; - } - - InkDropTransforms transforms; - root_layer_->SetVisible(true); - - switch (ink_drop_state_) { - case InkDropState::HIDDEN: - // This case is handled above in a short circuit return. - break; - case InkDropState::ACTION_PENDING: - CalculateCircleTransforms(large_size_, &transforms); - AnimateToTransforms(transforms, kVisibleOpacity, - GetAnimationDuration(InkDropState::ACTION_PENDING), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - animation_observer); - break; - case InkDropState::QUICK_ACTION: - CalculateCircleTransforms(large_size_, &transforms); - AnimateToTransforms(transforms, kHiddenOpacity, - GetAnimationDuration(InkDropState::QUICK_ACTION), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - animation_observer); - break; - case InkDropState::SLOW_ACTION_PENDING: - CalculateRectTransforms(small_size_, small_corner_radius_, &transforms); - AnimateToTransforms( - transforms, kVisibleOpacity, - GetAnimationDuration(InkDropState::SLOW_ACTION_PENDING), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - animation_observer); - break; - case InkDropState::SLOW_ACTION: - CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); - AnimateToTransforms(transforms, kHiddenOpacity, - GetAnimationDuration(InkDropState::SLOW_ACTION), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - animation_observer); - break; - case InkDropState::ACTIVATED: - CalculateRectTransforms(small_size_, small_corner_radius_, &transforms); - AnimateToTransforms(transforms, kVisibleOpacity, - GetAnimationDuration(InkDropState::ACTIVATED), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - animation_observer); - break; - case InkDropState::DEACTIVATED: - CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); - AnimateToTransforms(transforms, kHiddenOpacity, - GetAnimationDuration(InkDropState::DEACTIVATED), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - animation_observer); - break; - } -} -void InkDropAnimation::AnimateToTransforms( - const InkDropTransforms transforms, - float opacity, - base::TimeDelta duration, - ui::LayerAnimator::PreemptionStrategy preemption_strategy, - ui::LayerAnimationObserver* animation_observer) { - ui::LayerAnimator* root_animator = root_layer_->GetAnimator(); - ui::ScopedLayerAnimationSettings root_animation(root_animator); - root_animation.SetPreemptionStrategy(preemption_strategy); - ui::LayerAnimationElement* root_element = - ui::LayerAnimationElement::CreateOpacityElement(opacity, duration); - ui::LayerAnimationSequence* root_sequence = - new ui::LayerAnimationSequence(root_element); - - if (animation_observer) - root_sequence->AddObserver(animation_observer); - - root_animator->StartAnimation(root_sequence); - - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) { - ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator(); - ui::ScopedLayerAnimationSettings animation(animator); - animation.SetPreemptionStrategy(preemption_strategy); - ui::LayerAnimationElement* element = - ui::LayerAnimationElement::CreateTransformElement(transforms[i], - duration); - ui::LayerAnimationSequence* sequence = - new ui::LayerAnimationSequence(element); - - if (animation_observer) - sequence->AddObserver(animation_observer); - - animator->StartAnimation(sequence); - } -} - -void InkDropAnimation::SetStateToHidden() { - InkDropTransforms transforms; - // Using a size of 0x0 creates visual anomalies. - CalculateCircleTransforms(gfx::Size(1, 1), &transforms); - SetTransforms(transforms); - SetOpacity(kHiddenOpacity); -} - -void InkDropAnimation::SetTransforms(const InkDropTransforms transforms) { - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - painted_layers_[i]->SetTransform(transforms[i]); -} - -void InkDropAnimation::SetOpacity(float opacity) { - root_layer_->SetOpacity(opacity); -} - -void InkDropAnimation::CalculateCircleTransforms( - const gfx::Size& size, - InkDropTransforms* transforms_out) const { - CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f, - transforms_out); -} - -void InkDropAnimation::CalculateRectTransforms( - const gfx::Size& size, - float corner_radius, - InkDropTransforms* transforms_out) const { - DCHECK_GE(size.width() / 2.0f, corner_radius) - << "The circle's diameter should not be greater than the total width."; - DCHECK_GE(size.height() / 2.0f, corner_radius) - << "The circle's diameter should not be greater than the total height."; - - // The shapes are drawn such that their center points are not at the origin. - // Thus we use the CalculateCircleTransform() and CalculateRectTransform() - // methods to calculate the complex Transforms. - - const float circle_scale = std::max( - kMinimumCircleScale, - corner_radius / static_cast<float>(circle_layer_delegate_->radius())); - - const float circle_target_x_offset = size.width() / 2.0f - corner_radius; - const float circle_target_y_offset = size.height() / 2.0f - corner_radius; - - (*transforms_out)[TOP_LEFT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - -circle_target_x_offset, -circle_target_y_offset); - - (*transforms_out)[TOP_RIGHT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - circle_target_x_offset, -circle_target_y_offset); - - (*transforms_out)[BOTTOM_RIGHT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - circle_target_x_offset, circle_target_y_offset); - - (*transforms_out)[BOTTOM_LEFT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - -circle_target_x_offset, circle_target_y_offset); - - const float rect_delegate_width = - static_cast<float>(rect_layer_delegate_->size().width()); - const float rect_delegate_height = - static_cast<float>(rect_layer_delegate_->size().height()); - - (*transforms_out)[HORIZONTAL_RECT] = CalculateRectTransform( - ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), - std::max(kMinimumRectScale, size.width() / rect_delegate_width), - std::max(kMinimumRectScale, - (size.height() - 2.0f * corner_radius) / rect_delegate_height)); - - (*transforms_out)[VERTICAL_RECT] = CalculateRectTransform( - ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), - std::max(kMinimumRectScale, - (size.width() - 2.0f * corner_radius) / rect_delegate_width), - std::max(kMinimumRectScale, size.height() / rect_delegate_height)); + AnimateStateChange(old_ink_drop_state, target_ink_drop_state_, + animation_observer); + animation_observer->SetActive(); + // |this| may be deleted! |animation_observer| might synchronously call + // AnimationEndedCallback which can delete |this|. } -void InkDropAnimation::GetCurrentTansforms( - InkDropTransforms* transforms_out) const { - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - (*transforms_out)[i] = painted_layers_[i]->GetTargetTransform(); +void InkDropAnimation::SnapToActivated() { + AbortAllAnimations(); + // |animation_observer| will be deleted when AnimationEndedCallback() returns + // true. + // TODO(bruthig): Implement a safer ownership model for the + // |animation_observer|. + ui::CallbackLayerAnimationObserver* animation_observer = + new ui::CallbackLayerAnimationObserver( + base::Bind(&InkDropAnimation::AnimationStartedCallback, + base::Unretained(this), InkDropState::ACTIVATED), + base::Bind(&InkDropAnimation::AnimationEndedCallback, + base::Unretained(this), InkDropState::ACTIVATED)); + GetRootLayer()->SetVisible(true); + target_ink_drop_state_ = InkDropState::ACTIVATED; + animation_observer->SetActive(); } -void InkDropAnimation::AddPaintLayer(PaintedShape painted_shape) { - ui::LayerDelegate* delegate = nullptr; - switch (painted_shape) { - case TOP_LEFT_CIRCLE: - case TOP_RIGHT_CIRCLE: - case BOTTOM_RIGHT_CIRCLE: - case BOTTOM_LEFT_CIRCLE: - delegate = circle_layer_delegate_.get(); - break; - case HORIZONTAL_RECT: - case VERTICAL_RECT: - delegate = rect_layer_delegate_.get(); - break; - case PAINTED_SHAPE_COUNT: - NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type."; - break; - } - - ui::Layer* layer = new ui::Layer(); - root_layer_->Add(layer); - - layer->SetBounds(gfx::Rect(large_size_)); - layer->SetFillsBoundsOpaquely(false); - layer->set_delegate(delegate); - layer->SetVisible(true); - layer->SetOpacity(1.0); - layer->SetMasksToBounds(false); - layer->set_name("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape)); - - painted_layers_[painted_shape].reset(layer); +void InkDropAnimation::HideImmediately() { + AbortAllAnimations(); + SetStateToHidden(); + target_ink_drop_state_ = InkDropState::HIDDEN; } -void InkDropAnimation::AbortAllAnimations() { - root_layer_->GetAnimator()->AbortAllAnimations(); - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - painted_layers_[i]->GetAnimator()->AbortAllAnimations(); +test::InkDropAnimationTestApi* InkDropAnimation::GetTestApi() { + return nullptr; } void InkDropAnimation::AnimationStartedCallback( InkDropState ink_drop_state, const ui::CallbackLayerAnimationObserver& observer) { - FOR_EACH_OBSERVER(InkDropAnimationObserver, observers_, - InkDropAnimationStarted(ink_drop_state)); + observer_->AnimationStarted(ink_drop_state); } bool InkDropAnimation::AnimationEndedCallback( InkDropState ink_drop_state, const ui::CallbackLayerAnimationObserver& observer) { - FOR_EACH_OBSERVER( - InkDropAnimationObserver, observers_, - InkDropAnimationEnded(ink_drop_state, + if (ink_drop_state == InkDropState::HIDDEN) + SetStateToHidden(); + observer_->AnimationEnded(ink_drop_state, observer.aborted_count() - ? InkDropAnimationObserver::PRE_EMPTED - : InkDropAnimationObserver::SUCCESS)); + ? InkDropAnimationEndedReason::PRE_EMPTED + : InkDropAnimationEndedReason::SUCCESS); + // |this| may be deleted! return true; } diff --git a/chromium/ui/views/animation/ink_drop_animation.h b/chromium/ui/views/animation/ink_drop_animation.h index abcd48c9cad..b00be7db26f 100644 --- a/chromium/ui/views/animation/ink_drop_animation.h +++ b/chromium/ui/views/animation/ink_drop_animation.h @@ -1,4 +1,4 @@ -// Copyright 2015 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. @@ -6,13 +6,8 @@ #define UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_H_ #include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/observer_list.h" -#include "base/time/time.h" -#include "ui/compositor/layer_animator.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/geometry/size_f.h" -#include "ui/gfx/transform.h" +#include "ui/gfx/geometry/point.h" +#include "ui/views/animation/ink_drop_animation_observer.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/views_export.h" @@ -20,119 +15,95 @@ namespace ui { class CallbackLayerAnimationObserver; class Layer; class LayerAnimationObserver; -class LayerDelegate; } // namespace ui namespace views { -class CircleLayerDelegate; -class InkDropAnimationObserver; -class RectangleLayerDelegate; namespace test { class InkDropAnimationTestApi; } // namespace test -// An ink drop animation that smoothly animates between a circle and a rounded -// rectangle of different sizes for each of the different InkDropStates. The -// final frame for each InkDropState will be bounded by either a |large_size_| -// rectangle or a |small_size_| rectangle. +// Simple base class for animations that provide visual feedback for View state. +// Manages the attached InkDropAnimationObservers. // // TODO(bruthig): Document the ink drop ripple on chromium.org and add a link to -// it. +// the doc here. class VIEWS_EXPORT InkDropAnimation { public: - InkDropAnimation(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius); - ~InkDropAnimation(); + // TODO(bruthig): Remove UseFastAnimations() and kSlowAnimationDurationFactor. + // See http://crbug.com/584681 + + // Checks CommandLine switches to determine if the visual feedback should have + // a fast animations speed. + static bool UseFastAnimations(); + + // The factor at which to increase the animation durations if + // UseFastAnimations() returns true. + static const double kSlowAnimationDurationFactor; + + // The opacity of the ink drop when it is not visible. + static const float kHiddenOpacity; + + // The opacity of the ink drop when it is visible. + static const float kVisibleOpacity; + + InkDropAnimation(); + virtual ~InkDropAnimation(); + + // In the event that an animation is in progress for ink drop state 's1' and + // an animation to a new state 's2' is triggered, then + // AnimationEnded(s1, PRE_EMPTED) will be called before + // AnimationStarted(s2). + void set_observer(InkDropAnimationObserver* observer) { + observer_ = observer; + } + + // Animates from the current InkDropState to the new |ink_drop_state|. + // + // NOTE: GetTargetInkDropState() should return the new |ink_drop_state| value + // to any observers being notified as a result of the call. + void AnimateToState(InkDropState ink_drop_state); + + InkDropState target_ink_drop_state() const { return target_ink_drop_state_; } + + // Immediately aborts all in-progress animations and hides the ink drop. + // + // NOTE: This will NOT raise InkDropAnimation(Started|Ended) events for the + // state transition to HIDDEN! + void HideImmediately(); + + // Immediately snaps the ink drop to the ACTIVATED target state. All pending + // animations are aborted. Events will be raised for the pending animations + // as well as the transition to the ACTIVATED state. + virtual void SnapToActivated(); // The root Layer that can be added in to a Layer tree. - ui::Layer* root_layer() { return root_layer_.get(); } + virtual ui::Layer* GetRootLayer() = 0; - InkDropState ink_drop_state() const { return ink_drop_state_; } + // Returns true when the ripple is visible. This is different from checking if + // the ink_drop_state() == HIDDEN because the ripple may be visible while it + // animates to the target HIDDEN state. + virtual bool IsVisible() const = 0; - void AddObserver(InkDropAnimationObserver* observer); - void RemoveObserver(InkDropAnimationObserver* observer); + // Returns a test api to access internals of this. Default implmentations + // should return nullptr and test specific subclasses can override to return + // an instance. + virtual test::InkDropAnimationTestApi* GetTestApi(); - // Animates from the current |ink_drop_state_| to a new |ink_drop_state|. It - // is possible to animate from any |ink_drop_state_| to any new - // |ink_drop_state|. Note that some state transitions will also perform an - // implicit transition to the another state. e.g. AnimateToState(QUICK_ACTION) - // will implicitly transition to the HIDDEN state. - void AnimateToState(InkDropState ink_drop_state); + protected: + // Animates the ripple from the |old_ink_drop_state| to the + // |new_ink_drop_state|. |observer| is added to all LayerAnimationSequence's + // used if not null. + virtual void AnimateStateChange(InkDropState old_ink_drop_state, + InkDropState new_ink_drop_state, + ui::LayerAnimationObserver* observer) = 0; - // Sets the |center_point| of the ink drop layer relative to its parent Layer. - void SetCenterPoint(const gfx::Point& center_point); + // Updates the transforms, opacity, and visibility to a HIDDEN state. + virtual void SetStateToHidden() = 0; - private: - friend class test::InkDropAnimationTestApi; - - // Enumeration of the different shapes that compose the ink drop. - enum PaintedShape { - TOP_LEFT_CIRCLE = 0, - TOP_RIGHT_CIRCLE, - BOTTOM_RIGHT_CIRCLE, - BOTTOM_LEFT_CIRCLE, - HORIZONTAL_RECT, - VERTICAL_RECT, - // The total number of shapes, not an actual shape. - PAINTED_SHAPE_COUNT - }; - - // Returns a human readable string for the |painted_shape| value. - static std::string ToLayerName(PaintedShape painted_shape); - - // Type that contains a gfx::Tansform for each of the layers required by the - // ink drop. - typedef gfx::Transform InkDropTransforms[PAINTED_SHAPE_COUNT]; - - // Animates the ripple to |ink_drop_state| and attaches |observer| to all - // LayerAnimationSequence's used. - void AnimateToStateInternal(InkDropState ink_drop_state, - ui::LayerAnimationObserver* observer); - - // Animates all of the painted shape layers to the specified |transforms| and - // |opacity|. The animation will use the given |duration| and - // |preemption_strategy|, and |observer| will be added to all - // LayerAnimationSequences. - void AnimateToTransforms( - const InkDropTransforms transforms, - float opacity, - base::TimeDelta duration, - ui::LayerAnimator::PreemptionStrategy preemption_strategy, - ui::LayerAnimationObserver* observer); - - // Updates the Transforms and opacity to the HIDDEN state. - void SetStateToHidden(); - - // Sets the |transforms| on all of the shape layers. Note that this does not - // perform any animation. - void SetTransforms(const InkDropTransforms transforms); - - // Sets the opacity of the ink drop. - void SetOpacity(float opacity); - - // Updates all of the Transforms in |transforms_out| for a circle of the given - // |size|. - void CalculateCircleTransforms(const gfx::Size& size, - InkDropTransforms* transforms_out) const; - - // Updates all of the Transforms in |transforms_out| for a rounded rectangle - // of the given |size| and |corner_radius|. - void CalculateRectTransforms(const gfx::Size& size, - float corner_radius, - InkDropTransforms* transforms_out) const; - - // Updates all of the Transforms in |transforms_out| to the current target - // Transforms of the Layers. - void GetCurrentTansforms(InkDropTransforms* transforms_out) const; - - // Adds and configures a new |painted_shape| layer to |painted_layers_|. - void AddPaintLayer(PaintedShape painted_shape); - - void AbortAllAnimations(); + virtual void AbortAllAnimations() = 0; + private: // The Callback invoked when all of the animation sequences for the specific // |ink_drop_state| animation have started. |observer| is the // ui::CallbackLayerAnimationObserver which is notifying the callback. @@ -147,41 +118,10 @@ class VIEWS_EXPORT InkDropAnimation { InkDropState ink_drop_state, const ui::CallbackLayerAnimationObserver& observer); - // Maximum size that an ink drop will be drawn to for any InkDropState whose - // final frame should be large. - gfx::Size large_size_; - - // Corner radius used to draw the rounded rectangles corner for any - // InkDropState whose final frame should be large. - int large_corner_radius_; - - // Maximum size that an ink drop will be drawn to for any InkDropState whose - // final frame should be small. - gfx::Size small_size_; - - // Corner radius used to draw the rounded rectangles corner for any - // InkDropState whose final frame should be small. - int small_corner_radius_; - - // ui::LayerDelegate to paint circles for all the circle layers. - scoped_ptr<CircleLayerDelegate> circle_layer_delegate_; - - // ui::LayerDelegate to paint rectangles for all the rectangle layers. - scoped_ptr<RectangleLayerDelegate> rect_layer_delegate_; - - // The root layer that parents the animating layers. The root layer is used to - // manipulate opacity and location, and its children are used to manipulate - // the different painted shapes that compose the ink drop. - scoped_ptr<ui::Layer> root_layer_; - - // ui::Layers for all of the painted shape layers that compose the ink drop. - scoped_ptr<ui::Layer> painted_layers_[PAINTED_SHAPE_COUNT]; - - // The current ink drop state. - InkDropState ink_drop_state_; + // The target InkDropState. + InkDropState target_ink_drop_state_; - // List of observers to notify when animations have finished. - base::ObserverList<InkDropAnimationObserver> observers_; + InkDropAnimationObserver* observer_; DISALLOW_COPY_AND_ASSIGN(InkDropAnimation); }; diff --git a/chromium/ui/views/animation/ink_drop_animation_controller.h b/chromium/ui/views/animation/ink_drop_animation_controller.h index d4d4fce96bb..ec511e21986 100644 --- a/chromium/ui/views/animation/ink_drop_animation_controller.h +++ b/chromium/ui/views/animation/ink_drop_animation_controller.h @@ -21,28 +21,30 @@ class Layer; namespace views { -// Pure virtual base class that manages an ink drop animation's lifetime and -// state. +// Pure virtual base class that manages the lifetime and state of an ink drop +// animation as well as visual hover state feedback. class VIEWS_EXPORT InkDropAnimationController { public: virtual ~InkDropAnimationController() {} - // Gets the current state of the ink drop. - virtual InkDropState GetInkDropState() const = 0; + // Gets the target state of the ink drop. + virtual InkDropState GetTargetInkDropState() const = 0; + + // Returns true when the ripple is visible, including when animating to + // HIDDEN. + virtual bool IsVisible() const = 0; // Animates from the current InkDropState to |ink_drop_state|. virtual void AnimateToState(InkDropState ink_drop_state) = 0; - virtual gfx::Size GetInkDropLargeSize() const = 0; - - // Sets the different sizes of the ink drop. - virtual void SetInkDropSize(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius) = 0; + // Immediately snaps the InkDropState to ACTIVATED. This more specific + // implementation of the non-existent SnapToState(InkDropState) function is + // the only one available because it was the only InkDropState that clients + // needed to skip animations for. + virtual void SnapToActivated() = 0; - // Sets the |center_point| of the ink drop relative to its parent Layer. - virtual void SetInkDropCenter(const gfx::Point& center_point) = 0; + // Enables or disables the hover state. + virtual void SetHovered(bool is_hovered) = 0; protected: InkDropAnimationController() {} diff --git a/chromium/ui/views/animation/ink_drop_animation_controller_factory.cc b/chromium/ui/views/animation/ink_drop_animation_controller_factory.cc index 6a3f8a37bfc..19bae013b57 100644 --- a/chromium/ui/views/animation/ink_drop_animation_controller_factory.cc +++ b/chromium/ui/views/animation/ink_drop_animation_controller_factory.cc @@ -5,7 +5,7 @@ #include "ui/views/animation/ink_drop_animation_controller_factory.h" #include "base/macros.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/views/animation/ink_drop_animation_controller.h" @@ -25,14 +25,11 @@ class InkDropAnimationControllerStub ~InkDropAnimationControllerStub() override; // InkDropAnimationController: - InkDropState GetInkDropState() const override; + InkDropState GetTargetInkDropState() const override; + bool IsVisible() const override; void AnimateToState(InkDropState state) override; - gfx::Size GetInkDropLargeSize() const override; - void SetInkDropSize(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius) override; - void SetInkDropCenter(const gfx::Point& center_point) override; + void SnapToActivated() override; + void SetHovered(bool is_hovered) override; private: DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerStub); @@ -42,23 +39,19 @@ InkDropAnimationControllerStub::InkDropAnimationControllerStub() {} InkDropAnimationControllerStub::~InkDropAnimationControllerStub() {} -InkDropState InkDropAnimationControllerStub::GetInkDropState() const { +InkDropState InkDropAnimationControllerStub::GetTargetInkDropState() const { return InkDropState::HIDDEN; } -void InkDropAnimationControllerStub::AnimateToState(InkDropState state) {} - -gfx::Size InkDropAnimationControllerStub::GetInkDropLargeSize() const { - return gfx::Size(); +bool InkDropAnimationControllerStub::IsVisible() const { + return false; } -void InkDropAnimationControllerStub::SetInkDropSize(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius) {} +void InkDropAnimationControllerStub::AnimateToState(InkDropState state) {} + +void InkDropAnimationControllerStub::SnapToActivated() {} -void InkDropAnimationControllerStub::SetInkDropCenter( - const gfx::Point& center_point) {} +void InkDropAnimationControllerStub::SetHovered(bool is_hovered) {} } // namespace diff --git a/chromium/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc b/chromium/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc index 1743c59355d..c75b2c23d7b 100644 --- a/chromium/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc @@ -4,12 +4,16 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/test/test_mock_time_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/timer/timer.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/test/material_design_controller_test_api.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/views/animation/ink_drop_animation_controller.h" #include "ui/views/animation/ink_drop_animation_controller_factory.h" +#include "ui/views/animation/ink_drop_animation_controller_impl.h" #include "ui/views/animation/ink_drop_host.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/animation/test/test_ink_drop_host.h" @@ -17,7 +21,8 @@ namespace views { class InkDropAnimationControllerFactoryTest - : public testing::TestWithParam<ui::MaterialDesignController::Mode> { + : public testing::TestWithParam< + testing::tuple<ui::MaterialDesignController::Mode>> { public: InkDropAnimationControllerFactoryTest(); ~InkDropAnimationControllerFactoryTest(); @@ -31,8 +36,14 @@ class InkDropAnimationControllerFactoryTest scoped_ptr<InkDropAnimationController> ink_drop_animation_controller_; private: + // Extracts and returns the material design mode from the test parameters. + ui::MaterialDesignController::Mode GetMaterialMode() const; + scoped_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_; + // Required by base::Timer's. + scoped_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_; + DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerFactoryTest); }; @@ -42,16 +53,28 @@ InkDropAnimationControllerFactoryTest::InkDropAnimationControllerFactoryTest() // initialize and cache the mode. This ensures that these tests will run from // a non-initialized state. ui::test::MaterialDesignControllerTestAPI::UninitializeMode(); - ui::test::MaterialDesignControllerTestAPI::SetMode(GetParam()); + ui::test::MaterialDesignControllerTestAPI::SetMode(GetMaterialMode()); ink_drop_animation_controller_.reset( InkDropAnimationControllerFactory::CreateInkDropAnimationController( &test_ink_drop_host_) .release()); - ink_drop_animation_controller_->SetInkDropSize(gfx::Size(10, 10), 4, - gfx::Size(8, 8), 2); zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode( ui::ScopedAnimationDurationScaleMode::ZERO_DURATION)); + + switch (GetMaterialMode()) { + case ui::MaterialDesignController::NON_MATERIAL: + break; + case ui::MaterialDesignController::MATERIAL_NORMAL: + case ui::MaterialDesignController::MATERIAL_HYBRID: + // The Timer's used by the InkDropAnimationControllerImpl class require a + // base::ThreadTaskRunnerHandle instance. + scoped_refptr<base::TestMockTimeTaskRunner> task_runner( + new base::TestMockTimeTaskRunner); + thread_task_runner_handle_.reset( + new base::ThreadTaskRunnerHandle(task_runner)); + break; + } } InkDropAnimationControllerFactoryTest:: @@ -59,56 +82,72 @@ InkDropAnimationControllerFactoryTest:: ui::test::MaterialDesignControllerTestAPI::UninitializeMode(); } +ui::MaterialDesignController::Mode +InkDropAnimationControllerFactoryTest::GetMaterialMode() const { + return testing::get<0>(GetParam()); +} + // Note: First argument is optional and intentionally left blank. // (it's a prefix for the generated test cases) INSTANTIATE_TEST_CASE_P( , InkDropAnimationControllerFactoryTest, testing::Values(ui::MaterialDesignController::NON_MATERIAL, - ui::MaterialDesignController::MATERIAL_NORMAL)); + ui::MaterialDesignController::MATERIAL_NORMAL, + ui::MaterialDesignController::MATERIAL_HYBRID)); TEST_P(InkDropAnimationControllerFactoryTest, - VerifyAllInkDropLayersRemovedAfterDestruction) { + VerifyInkDropLayersRemovedAfterDestructionWhenRippleIsActive) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); ink_drop_animation_controller_.reset(); EXPECT_EQ(0, test_ink_drop_host_.num_ink_drop_layers()); } +TEST_P(InkDropAnimationControllerFactoryTest, + VerifyInkDropLayersRemovedAfterDestructionWhenHoverIsActive) { + test_ink_drop_host_.set_should_show_hover(true); + ink_drop_animation_controller_->SetHovered(true); + ink_drop_animation_controller_.reset(); + EXPECT_EQ(0, test_ink_drop_host_.num_ink_drop_layers()); +} + TEST_P(InkDropAnimationControllerFactoryTest, StateIsHiddenInitially) { EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalQuickAction) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); - ink_drop_animation_controller_->AnimateToState(InkDropState::QUICK_ACTION); + ink_drop_animation_controller_->AnimateToState( + InkDropState::ACTION_TRIGGERED); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, CancelQuickAction) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); ink_drop_animation_controller_->AnimateToState(InkDropState::HIDDEN); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalSlowAction) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); ink_drop_animation_controller_->AnimateToState( - InkDropState::SLOW_ACTION_PENDING); - ink_drop_animation_controller_->AnimateToState(InkDropState::SLOW_ACTION); + InkDropState::ALTERNATE_ACTION_PENDING); + ink_drop_animation_controller_->AnimateToState( + InkDropState::ALTERNATE_ACTION_TRIGGERED); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, CancelSlowAction) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); ink_drop_animation_controller_->AnimateToState( - InkDropState::SLOW_ACTION_PENDING); + InkDropState::ALTERNATE_ACTION_PENDING); ink_drop_animation_controller_->AnimateToState(InkDropState::HIDDEN); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalQuickActivated) { @@ -116,17 +155,17 @@ TEST_P(InkDropAnimationControllerFactoryTest, TypicalQuickActivated) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTIVATED); ink_drop_animation_controller_->AnimateToState(InkDropState::DEACTIVATED); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalSlowActivated) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); ink_drop_animation_controller_->AnimateToState( - InkDropState::SLOW_ACTION_PENDING); + InkDropState::ALTERNATE_ACTION_PENDING); ink_drop_animation_controller_->AnimateToState(InkDropState::ACTIVATED); ink_drop_animation_controller_->AnimateToState(InkDropState::DEACTIVATED); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_animation_controller_impl.cc b/chromium/ui/views/animation/ink_drop_animation_controller_impl.cc index 74d681d8d63..62e953ba606 100644 --- a/chromium/ui/views/animation/ink_drop_animation_controller_impl.cc +++ b/chromium/ui/views/animation/ink_drop_animation_controller_impl.cc @@ -4,104 +4,256 @@ #include "ui/views/animation/ink_drop_animation_controller_impl.h" -#include "ui/views/animation/ink_drop_animation.h" +#include "base/auto_reset.h" +#include "base/timer/timer.h" +#include "ui/compositor/layer.h" #include "ui/views/animation/ink_drop_host.h" +#include "ui/views/animation/ink_drop_hover.h" +#include "ui/views/animation/square_ink_drop_animation.h" namespace views { +namespace { + +// The duration, in milliseconds, of the hover state fade in animation when it +// is triggered by user input. +const int kHoverFadeInFromUserInputDurationInMs = 250; + +// The duration, in milliseconds, of the hover state fade out animation when it +// is triggered by user input. +const int kHoverFadeOutFromUserInputDurationInMs = 250; + +// The duration, in milliseconds, of the hover state fade in animation when it +// is triggered by an ink drop ripple animation ending. +const int kHoverFadeInAfterAnimationDurationInMs = 250; + +// The duration, in milliseconds, of the hover state fade out animation when it +// is triggered by an ink drop ripple animation starting. +const int kHoverFadeOutBeforeAnimationDurationInMs = 120; + +// The amount of time in milliseconds that |hover_| should delay after a ripple +// animation before fading in. +const int kHoverFadeInAfterAnimationDelayInMs = 1000; + +// Returns true if an ink drop with the given |ink_drop_state| should +// automatically transition to the InkDropState::HIDDEN state. +bool ShouldAnimateToHidden(InkDropState ink_drop_state) { + switch (ink_drop_state) { + case views::InkDropState::ACTION_TRIGGERED: + case views::InkDropState::ALTERNATE_ACTION_TRIGGERED: + case views::InkDropState::DEACTIVATED: + return true; + default: + return false; + } +} + +} // namespace + InkDropAnimationControllerImpl::InkDropAnimationControllerImpl( InkDropHost* ink_drop_host) - : ink_drop_host_(ink_drop_host) {} + : ink_drop_host_(ink_drop_host), + root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)), + root_layer_added_to_host_(false), + is_hovered_(false), + hover_after_animation_timer_(nullptr) { + root_layer_->set_name("InkDropAnimationControllerImpl:RootLayer"); +} InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() { // Explicitly destroy the InkDropAnimation so that this still exists if // views::InkDropAnimationObserver methods are called on this. DestroyInkDropAnimation(); + DestroyInkDropHover(); } -InkDropState InkDropAnimationControllerImpl::GetInkDropState() const { +InkDropState InkDropAnimationControllerImpl::GetTargetInkDropState() const { if (!ink_drop_animation_) return InkDropState::HIDDEN; - return ink_drop_animation_->ink_drop_state(); + return ink_drop_animation_->target_ink_drop_state(); +} + +bool InkDropAnimationControllerImpl::IsVisible() const { + return ink_drop_animation_ && ink_drop_animation_->IsVisible(); } void InkDropAnimationControllerImpl::AnimateToState( InkDropState ink_drop_state) { + DestroyHiddenTargetedAnimations(); if (!ink_drop_animation_) CreateInkDropAnimation(); + + if (ink_drop_state != views::InkDropState::HIDDEN) { + SetHoveredInternal(false, base::TimeDelta::FromMilliseconds( + kHoverFadeOutBeforeAnimationDurationInMs), + true); + } + ink_drop_animation_->AnimateToState(ink_drop_state); } -gfx::Size InkDropAnimationControllerImpl::GetInkDropLargeSize() const { - return ink_drop_large_size_; +void InkDropAnimationControllerImpl::SnapToActivated() { + DestroyHiddenTargetedAnimations(); + if (!ink_drop_animation_) + CreateInkDropAnimation(); + + SetHoveredInternal(false, base::TimeDelta(), false); + + ink_drop_animation_->SnapToActivated(); } -void InkDropAnimationControllerImpl::SetInkDropSize(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius) { - // TODO(bruthig): Fix the ink drop animations to work for non-square sizes. - DCHECK_EQ(large_size.width(), large_size.height()) - << "The ink drop animation does not currently support non-square sizes."; - DCHECK_EQ(small_size.width(), small_size.height()) - << "The ink drop animation does not currently support non-square sizes."; - ink_drop_large_size_ = large_size; - ink_drop_large_corner_radius_ = large_corner_radius; - ink_drop_small_size_ = small_size; - ink_drop_small_corner_radius_ = small_corner_radius; - ink_drop_animation_.reset(); +void InkDropAnimationControllerImpl::SetHovered(bool is_hovered) { + is_hovered_ = is_hovered; + SetHoveredInternal(is_hovered, + is_hovered ? base::TimeDelta::FromMilliseconds( + kHoverFadeInFromUserInputDurationInMs) + : base::TimeDelta::FromMilliseconds( + kHoverFadeOutFromUserInputDurationInMs), + false); } -void InkDropAnimationControllerImpl::SetInkDropCenter( - const gfx::Point& center_point) { - ink_drop_center_ = center_point; - if (ink_drop_animation_) - ink_drop_animation_->SetCenterPoint(ink_drop_center_); +void InkDropAnimationControllerImpl::DestroyHiddenTargetedAnimations() { + if (ink_drop_animation_ && + (ink_drop_animation_->target_ink_drop_state() == InkDropState::HIDDEN || + ShouldAnimateToHidden(ink_drop_animation_->target_ink_drop_state()))) { + DestroyInkDropAnimation(); + } } void InkDropAnimationControllerImpl::CreateInkDropAnimation() { DestroyInkDropAnimation(); - - ink_drop_animation_.reset(new InkDropAnimation( - ink_drop_large_size_, ink_drop_large_corner_radius_, ink_drop_small_size_, - ink_drop_small_corner_radius_)); - - ink_drop_animation_->AddObserver(this); - ink_drop_animation_->SetCenterPoint(ink_drop_center_); - ink_drop_host_->AddInkDropLayer(ink_drop_animation_->root_layer()); + ink_drop_animation_ = ink_drop_host_->CreateInkDropAnimation(); + ink_drop_animation_->set_observer(this); + root_layer_->Add(ink_drop_animation_->GetRootLayer()); + AddRootLayerToHostIfNeeded(); } void InkDropAnimationControllerImpl::DestroyInkDropAnimation() { if (!ink_drop_animation_) return; - ink_drop_host_->RemoveInkDropLayer(ink_drop_animation_->root_layer()); - ink_drop_animation_->RemoveObserver(this); + root_layer_->Remove(ink_drop_animation_->GetRootLayer()); ink_drop_animation_.reset(); + RemoveRootLayerFromHostIfNeeded(); +} + +void InkDropAnimationControllerImpl::CreateInkDropHover() { + DestroyInkDropHover(); + + hover_ = ink_drop_host_->CreateInkDropHover(); + if (!hover_) + return; + hover_->set_observer(this); + root_layer_->Add(hover_->layer()); + AddRootLayerToHostIfNeeded(); +} + +void InkDropAnimationControllerImpl::DestroyInkDropHover() { + if (!hover_) + return; + root_layer_->Remove(hover_->layer()); + hover_->set_observer(nullptr); + hover_.reset(); + RemoveRootLayerFromHostIfNeeded(); +} + +void InkDropAnimationControllerImpl::AddRootLayerToHostIfNeeded() { + DCHECK(hover_ || ink_drop_animation_); + if (!root_layer_added_to_host_) { + root_layer_added_to_host_ = true; + ink_drop_host_->AddInkDropLayer(root_layer_.get()); + } +} + +void InkDropAnimationControllerImpl::RemoveRootLayerFromHostIfNeeded() { + if (root_layer_added_to_host_ && !hover_ && !ink_drop_animation_) { + root_layer_added_to_host_ = false; + ink_drop_host_->RemoveInkDropLayer(root_layer_.get()); + } +} + +bool InkDropAnimationControllerImpl::IsHoverFadingInOrVisible() const { + return hover_ && hover_->IsFadingInOrVisible(); } -void InkDropAnimationControllerImpl::InkDropAnimationStarted( +// ----------------------------------------------------------------------------- +// views::InkDropAnimationObserver: + +void InkDropAnimationControllerImpl::AnimationStarted( InkDropState ink_drop_state) {} -void InkDropAnimationControllerImpl::InkDropAnimationEnded( +void InkDropAnimationControllerImpl::AnimationEnded( InkDropState ink_drop_state, InkDropAnimationEndedReason reason) { - if (reason != SUCCESS) + if (reason != InkDropAnimationEndedReason::SUCCESS) return; - switch (ink_drop_state) { - case views::InkDropState::QUICK_ACTION: - case views::InkDropState::SLOW_ACTION: - case views::InkDropState::DEACTIVATED: - ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN); - break; - case views::InkDropState::HIDDEN: - // TODO(bruthig): Investigate whether creating and destroying - // InkDropAnimations is expensive and consider creating an - // InkDropAnimationPool. See www.crbug.com/522175. - DestroyInkDropAnimation(); - break; - default: - break; + if (ShouldAnimateToHidden(ink_drop_state)) { + ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN); + } else if (ink_drop_state == views::InkDropState::HIDDEN) { + if (is_hovered_) + StartHoverAfterAnimationTimer(); + // TODO(bruthig): Investigate whether creating and destroying + // InkDropAnimations is expensive and consider creating an + // InkDropAnimationPool. See www.crbug.com/522175. + DestroyInkDropAnimation(); } } +// ----------------------------------------------------------------------------- +// views::InkDropHoverObserver: + +void InkDropAnimationControllerImpl::AnimationStarted( + InkDropHover::AnimationType animation_type) {} + +void InkDropAnimationControllerImpl::AnimationEnded( + InkDropHover::AnimationType animation_type, + InkDropAnimationEndedReason reason) { + if (animation_type == InkDropHover::FADE_OUT && + reason == InkDropAnimationEndedReason::SUCCESS) { + DestroyInkDropHover(); + } +} + +void InkDropAnimationControllerImpl::SetHoveredInternal( + bool is_hovered, + base::TimeDelta animation_duration, + bool explode) { + StopHoverAfterAnimationTimer(); + + if (IsHoverFadingInOrVisible() == is_hovered) + return; + + if (is_hovered) { + CreateInkDropHover(); + if (hover_ && !IsVisible()) + hover_->FadeIn(animation_duration); + } else { + hover_->FadeOut(animation_duration, explode); + } +} + +void InkDropAnimationControllerImpl::StartHoverAfterAnimationTimer() { + StopHoverAfterAnimationTimer(); + + if (!hover_after_animation_timer_) + hover_after_animation_timer_.reset(new base::OneShotTimer); + + hover_after_animation_timer_->Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kHoverFadeInAfterAnimationDelayInMs), + base::Bind(&InkDropAnimationControllerImpl::HoverAfterAnimationTimerFired, + base::Unretained(this))); +} + +void InkDropAnimationControllerImpl::StopHoverAfterAnimationTimer() { + if (hover_after_animation_timer_) + hover_after_animation_timer_.reset(); +} + +void InkDropAnimationControllerImpl::HoverAfterAnimationTimerFired() { + SetHoveredInternal(true, base::TimeDelta::FromMilliseconds( + kHoverFadeInAfterAnimationDurationInMs), + true); + hover_after_animation_timer_.reset(); +} + } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_animation_controller_impl.h b/chromium/ui/views/animation/ink_drop_animation_controller_impl.h index c5338835b0d..f39e4b9dd8a 100644 --- a/chromium/ui/views/animation/ink_drop_animation_controller_impl.h +++ b/chromium/ui/views/animation/ink_drop_animation_controller_impl.h @@ -11,16 +11,28 @@ #include "ui/gfx/geometry/size.h" #include "ui/views/animation/ink_drop_animation_controller.h" #include "ui/views/animation/ink_drop_animation_observer.h" +#include "ui/views/animation/ink_drop_hover_observer.h" #include "ui/views/views_export.h" +namespace base { +class Timer; +} // namespace base + namespace views { +namespace test { +class InkDropAnimationControllerImplTestApi; +} // namespace test + class InkDropAnimation; class InkDropHost; +class InkDropHover; +class InkDropAnimationControllerFactoryTest; // A functional implementation of an InkDropAnimationController. class VIEWS_EXPORT InkDropAnimationControllerImpl : public InkDropAnimationController, - public InkDropAnimationObserver { + public InkDropAnimationObserver, + public InkDropHoverObserver { public: // Constructs an ink drop controller that will attach the ink drop to the // given |ink_drop_host|. @@ -28,16 +40,18 @@ class VIEWS_EXPORT InkDropAnimationControllerImpl ~InkDropAnimationControllerImpl() override; // InkDropAnimationController: - InkDropState GetInkDropState() const override; + InkDropState GetTargetInkDropState() const override; + bool IsVisible() const override; void AnimateToState(InkDropState ink_drop_state) override; - gfx::Size GetInkDropLargeSize() const override; - void SetInkDropSize(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius) override; - void SetInkDropCenter(const gfx::Point& center_point) override; + void SnapToActivated() override; + void SetHovered(bool is_hovered) override; private: + friend class test::InkDropAnimationControllerImplTestApi; + + // Destroys |ink_drop_animation_| if it's targeted to the HIDDEN state. + void DestroyHiddenTargetedAnimations(); + // Creates a new InkDropAnimation and sets it to |ink_drop_animation_|. If // |ink_drop_animation_| wasn't null then it will be destroyed using // DestroyInkDropAnimation(). @@ -46,33 +60,79 @@ class VIEWS_EXPORT InkDropAnimationControllerImpl // Destroys the current |ink_drop_animation_|. void DestroyInkDropAnimation(); - // views::InkDropAnimationObserver: - void InkDropAnimationStarted(InkDropState ink_drop_state) override; - void InkDropAnimationEnded(InkDropState ink_drop_state, - InkDropAnimationEndedReason reason) override; + // Creates a new InkDropHover and sets it to |hover_|. If |hover_| wasn't null + // then it will be destroyed using DestroyInkDropHover(). + void CreateInkDropHover(); - // The host of the ink drop. - InkDropHost* ink_drop_host_; + // Destroys the current |hover_|. + void DestroyInkDropHover(); - // Cached size for the ink drop's large size animations. - gfx::Size ink_drop_large_size_; + // Adds the |root_layer_| to the |ink_drop_host_| if it hasn't already been + // added. + void AddRootLayerToHostIfNeeded(); - // Cached corner radius for the ink drop's large size animations. - int ink_drop_large_corner_radius_; + // Removes the |root_layer_| from the |ink_drop_host_| if no ink drop ripple + // or hover is active. + void RemoveRootLayerFromHostIfNeeded(); - // Cached size for the ink drop's small size animations. - gfx::Size ink_drop_small_size_; + // Returns true if the hover animation is in the process of fading in or + // is visible. + bool IsHoverFadingInOrVisible() const; - // Cached corner radius for the ink drop's small size animations. - int ink_drop_small_corner_radius_; + // views::InkDropAnimationObserver: + void AnimationStarted(InkDropState ink_drop_state) override; + void AnimationEnded(InkDropState ink_drop_state, + InkDropAnimationEndedReason reason) override; + + // views::InkDropHoverObserver: + void AnimationStarted(InkDropHover::AnimationType animation_type) override; + void AnimationEnded(InkDropHover::AnimationType animation_type, + InkDropAnimationEndedReason reason) override; + + // Enables or disables the hover state based on |is_hovered| and if an + // animation is triggered it will be scheduled to have the given + // |animation_duration|. If |explode| is true the hover will expand as it + // fades out. |explode| is ignored when |is_hovered| is true. + void SetHoveredInternal(bool is_hovered, + base::TimeDelta animation_duration, + bool explode); + + // Starts the |hover_after_animation_timer_| timer. This will stop the current + // |hover_after_animation_timer_| instance if it exists. + void StartHoverAfterAnimationTimer(); + + // Stops and destroys the current |hover_after_animation_timer_| instance. + void StopHoverAfterAnimationTimer(); + + // Callback for when the |hover_after_animation_timer_| fires. + void HoverAfterAnimationTimerFired(); + + // The host of the ink drop. Used to poll for information such as whether the + // hover should be shown or not. + InkDropHost* ink_drop_host_; + + // The root Layer that parents the InkDropAnimation layers and the + // InkDropHover layers. The |root_layer_| is the one that is added and removed + // from the InkDropHost. + scoped_ptr<ui::Layer> root_layer_; - // Cached center point for the ink drop. - gfx::Point ink_drop_center_; + // True when the |root_layer_| has been added to the |ink_drop_host_|. + bool root_layer_added_to_host_; + + // The current InkDropHover. Lazily created using CreateInkDropHover(); + scoped_ptr<InkDropHover> hover_; + + // Tracks the logical hovered state of |this| as manipulated by the public + // SetHovered() function. + bool is_hovered_; // The current InkDropAnimation. Created on demand using // CreateInkDropAnimation(). scoped_ptr<InkDropAnimation> ink_drop_animation_; + // The timer used to delay the hover fade in after an ink drop animation. + scoped_ptr<base::Timer> hover_after_animation_timer_; + DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerImpl); }; diff --git a/chromium/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc b/chromium/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc new file mode 100644 index 00000000000..75e1d4cea76 --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc @@ -0,0 +1,241 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/animation/ink_drop_animation_controller_impl.h" + +#include "base/macros.h" +#include "base/test/test_simple_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/animation/ink_drop_animation.h" +#include "ui/views/animation/test/ink_drop_animation_controller_impl_test_api.h" +#include "ui/views/animation/test/test_ink_drop_host.h" + +namespace views { + +// NOTE: The InkDropAnimationControllerImpl class is also tested by the +// InkDropAnimationControllerFactoryTest tests. +class InkDropAnimationControllerImplTest : public testing::Test { + public: + InkDropAnimationControllerImplTest(); + ~InkDropAnimationControllerImplTest() override; + + protected: + TestInkDropHost ink_drop_host_; + + // The test target. + InkDropAnimationControllerImpl ink_drop_animation_controller_; + + // Allows privileged access to the the |ink_drop_hover_|. + test::InkDropAnimationControllerImplTestApi test_api_; + + // Used to control the tasks scheduled by the InkDropAnimationControllerImpl's + // Timer. + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + + // Required by base::Timer's. + scoped_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_; + + private: + DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerImplTest); +}; + +InkDropAnimationControllerImplTest::InkDropAnimationControllerImplTest() + : ink_drop_animation_controller_(&ink_drop_host_), + test_api_(&ink_drop_animation_controller_), + task_runner_(new base::TestSimpleTaskRunner), + thread_task_runner_handle_( + new base::ThreadTaskRunnerHandle(task_runner_)) { + ink_drop_host_.set_disable_timers_for_test(true); +} + +InkDropAnimationControllerImplTest::~InkDropAnimationControllerImplTest() {} + +TEST_F(InkDropAnimationControllerImplTest, SetHoveredIsFadingInOrVisible) { + ink_drop_host_.set_should_show_hover(true); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_TRUE(test_api_.IsHoverFadingInOrVisible()); + + test_api_.CompleteAnimations(); + + ink_drop_animation_controller_.SetHovered(false); + EXPECT_FALSE(test_api_.IsHoverFadingInOrVisible()); +} + +TEST_F(InkDropAnimationControllerImplTest, + HoverDoesntFadeInAfterAnimationIfHoverNotSet) { + ink_drop_host_.set_should_show_hover(true); + ink_drop_animation_controller_.SetHovered(false); + ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_TRIGGERED); + test_api_.CompleteAnimations(); + + EXPECT_FALSE(task_runner_->HasPendingTask()); + EXPECT_FALSE(test_api_.IsHoverFadingInOrVisible()); +} + +TEST_F(InkDropAnimationControllerImplTest, + HoverFadesInAfterAnimationWhenHostIsHovered) { + ink_drop_host_.set_should_show_hover(true); + ink_drop_animation_controller_.SetHovered(true); + ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_TRIGGERED); + test_api_.CompleteAnimations(); + + EXPECT_TRUE(task_runner_->HasPendingTask()); + + task_runner_->RunPendingTasks(); + + EXPECT_TRUE(test_api_.IsHoverFadingInOrVisible()); +} + +TEST_F(InkDropAnimationControllerImplTest, + HoverDoesntFadeInAfterAnimationWhenHostIsNotHovered) { + ink_drop_host_.set_should_show_hover(false); + ink_drop_animation_controller_.SetHovered(true); + ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_TRIGGERED); + test_api_.CompleteAnimations(); + + EXPECT_TRUE(task_runner_->HasPendingTask()); + + task_runner_->RunPendingTasks(); + + EXPECT_FALSE(test_api_.IsHoverFadingInOrVisible()); +} + +TEST_F(InkDropAnimationControllerImplTest, + HoveredStateNotVisibleOrFadingInAfterAnimateToState) { + ink_drop_host_.set_should_show_hover(true); + + ink_drop_animation_controller_.SetHovered(true); + test_api_.CompleteAnimations(); + EXPECT_TRUE(test_api_.IsHoverFadingInOrVisible()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_TRIGGERED); + EXPECT_FALSE(test_api_.IsHoverFadingInOrVisible()); +} + +// Verifies that there is not a crash when setting hovered state and the host +// returns null for the hover. +TEST_F(InkDropAnimationControllerImplTest, + SetHoveredFalseWorksWhenNoInkDropHoverExists) { + ink_drop_host_.set_should_show_hover(false); + ink_drop_animation_controller_.SetHovered(true); + EXPECT_FALSE(test_api_.hover()); + ink_drop_animation_controller_.SetHovered(false); + EXPECT_FALSE(test_api_.hover()); +} + +TEST_F(InkDropAnimationControllerImplTest, HoverFadesOutOnSnapToActivated) { + ink_drop_host_.set_should_show_hover(true); + ink_drop_animation_controller_.SetHovered(true); + test_api_.CompleteAnimations(); + + EXPECT_TRUE(test_api_.IsHoverFadingInOrVisible()); + + ink_drop_animation_controller_.SnapToActivated(); + + EXPECT_FALSE(test_api_.IsHoverFadingInOrVisible()); +} + +TEST_F(InkDropAnimationControllerImplTest, LayersRemovedFromHostAfterHover) { + ink_drop_host_.set_should_show_hover(true); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + test_api_.CompleteAnimations(); + + ink_drop_animation_controller_.SetHovered(false); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + test_api_.CompleteAnimations(); + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); +} + +TEST_F(InkDropAnimationControllerImplTest, LayersRemovedFromHostAfterInkDrop) { + ink_drop_host_.set_should_show_hover(true); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::HIDDEN); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + test_api_.CompleteAnimations(); + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); +} + +TEST_F(InkDropAnimationControllerImplTest, + LayersAddedToHostWhenHoverOrInkDropVisible) { + ink_drop_host_.set_should_show_hover(true); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::HIDDEN); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + test_api_.CompleteAnimations(); + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + EXPECT_TRUE(task_runner_->HasPendingTask()); + task_runner_->RunPendingTasks(); + + // Hover should be fading back in. + EXPECT_TRUE(test_api_.HasActiveAnimations()); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); +} + +TEST_F(InkDropAnimationControllerImplTest, + LayersNotAddedToHostWhenHoverTimeFires) { + ink_drop_host_.set_should_show_hover(true); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::HIDDEN); + test_api_.CompleteAnimations(); + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_host_.set_should_show_hover(false); + + EXPECT_TRUE(task_runner_->HasPendingTask()); + task_runner_->RunPendingTasks(); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); +} + +TEST_F(InkDropAnimationControllerImplTest, + LayersArentRemovedWhenPreemptingFadeOut) { + ink_drop_host_.set_should_show_hover(true); + + EXPECT_EQ(0, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + test_api_.CompleteAnimations(); + + ink_drop_animation_controller_.SetHovered(false); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_EQ(1, ink_drop_host_.num_ink_drop_layers()); +} + +} // namespace views diff --git a/chromium/ui/views/animation/ink_drop_animation_ended_reason.cc b/chromium/ui/views/animation/ink_drop_animation_ended_reason.cc new file mode 100644 index 00000000000..8499995cd34 --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_animation_ended_reason.cc @@ -0,0 +1,23 @@ +// 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/views/animation/ink_drop_animation_ended_reason.h" + +#include "base/logging.h" + +namespace views { + +std::string ToString(InkDropAnimationEndedReason reason) { + switch (reason) { + case InkDropAnimationEndedReason::SUCCESS: + return "SUCCESS"; + case InkDropAnimationEndedReason::PRE_EMPTED: + return "PRE_EMPTED"; + } + NOTREACHED() + << "Should never be reached but is necessary for some compilers."; + return std::string(); +} + +} // namespace views diff --git a/chromium/ui/views/animation/ink_drop_animation_ended_reason.h b/chromium/ui/views/animation/ink_drop_animation_ended_reason.h new file mode 100644 index 00000000000..fc2f359cef2 --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_animation_ended_reason.h @@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_ENDED_REASON_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_ENDED_REASON_H_ + +#include <string> + +namespace views { + +// Enumeration of the different reasons why an ink drop animation has finished. +enum class InkDropAnimationEndedReason { + // The animation was completed successfully. + SUCCESS, + // The animation was stopped prematurely before reaching its final state. + PRE_EMPTED +}; + +// Returns a human readable string for |reason|. Useful for logging. +std::string ToString(InkDropAnimationEndedReason reason); + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_ENDED_REASON_H_ diff --git a/chromium/ui/views/animation/ink_drop_animation_observer.cc b/chromium/ui/views/animation/ink_drop_animation_observer.cc deleted file mode 100644 index c76ffb291b1..00000000000 --- a/chromium/ui/views/animation/ink_drop_animation_observer.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/animation/ink_drop_animation_observer.h" - -#include <string> - -#include "base/logging.h" - -namespace views { - -std::string ToString( - InkDropAnimationObserver::InkDropAnimationEndedReason reason) { - switch (reason) { - case InkDropAnimationObserver::SUCCESS: - return std::string("SUCCESS"); - case InkDropAnimationObserver::PRE_EMPTED: - return std::string("PRE_EMPTED"); - } - NOTREACHED() - << "Should never be reached but is necessary for some compilers."; - return std::string(); -} - -} // namespace views diff --git a/chromium/ui/views/animation/ink_drop_animation_observer.h b/chromium/ui/views/animation/ink_drop_animation_observer.h index 3eb3b1d2412..831a6be06b3 100644 --- a/chromium/ui/views/animation/ink_drop_animation_observer.h +++ b/chromium/ui/views/animation/ink_drop_animation_observer.h @@ -8,49 +8,34 @@ #include <string> #include "base/macros.h" +#include "ui/views/animation/ink_drop_animation_ended_reason.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/views_export.h" namespace views { -// Pure-virtual base class of an observer that can be attached to -// InkDropAnimations. +// Observer to attach to an InkDropAnimation. class VIEWS_EXPORT InkDropAnimationObserver { public: - // Enumeration of the different reasons why an InkDropAnimation has finished. - enum InkDropAnimationEndedReason { - // The animation was completed successfully. - SUCCESS, - // The animation was stopped prematurely before reaching its final state. - PRE_EMPTED - }; - - // Notifies the observer that an animation for |ink_drop_state| has started. - virtual void InkDropAnimationStarted(InkDropState ink_drop_state) = 0; - - // Notifies the observer that an animation for |ink_drop_state| has finished - // and the reason for completion is given by |reason|. If |reason| is SUCCESS - // then the animation has progressed to its final frame however if |reason| - // is |PRE_EMPTED| then the animation was stopped before its final frame. In - // the event that an animation is in progress for ink drop state 's1' and an - // animation to a new state 's2' is triggered, then - // InkDropAnimationEnded(s1, PRE_EMPTED) will be called before - // InkDropAnimationStarted(s2). - virtual void InkDropAnimationEnded(InkDropState ink_drop_state, - InkDropAnimationEndedReason reason) = 0; + // An animation for the given |ink_drop_state| has started. + virtual void AnimationStarted(InkDropState ink_drop_state) = 0; + + // Notifies the observer that an animation for the given |ink_drop_state| has + // finished and the reason for completion is given by |reason|. If |reason| is + // SUCCESS then the animation has progressed to its final frame however if + // |reason| is |PRE_EMPTED| then the animation was stopped before its final + // frame. + virtual void AnimationEnded(InkDropState ink_drop_state, + InkDropAnimationEndedReason reason) = 0; protected: - InkDropAnimationObserver() {} - virtual ~InkDropAnimationObserver() {} + InkDropAnimationObserver() = default; + virtual ~InkDropAnimationObserver() = default; private: DISALLOW_COPY_AND_ASSIGN(InkDropAnimationObserver); }; -// Returns a human readable string for |reason|. Useful for logging. -std::string ToString( - InkDropAnimationObserver::InkDropAnimationEndedReason reason); - } // namespace views #endif // UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_OBSERVER_H_ diff --git a/chromium/ui/views/animation/ink_drop_animation_unittest.cc b/chromium/ui/views/animation/ink_drop_animation_unittest.cc index 91f8143416a..e1ff62c456c 100644 --- a/chromium/ui/views/animation/ink_drop_animation_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_animation_unittest.cc @@ -9,293 +9,343 @@ #include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/size.h" -#include "ui/gfx/geometry/size_f.h" +#include "ui/views/animation/flood_fill_ink_drop_animation.h" #include "ui/views/animation/ink_drop_animation.h" +#include "ui/views/animation/ink_drop_animation_observer.h" #include "ui/views/animation/ink_drop_state.h" +#include "ui/views/animation/square_ink_drop_animation.h" +#include "ui/views/animation/test/flood_fill_ink_drop_animation_test_api.h" #include "ui/views/animation/test/ink_drop_animation_test_api.h" +#include "ui/views/animation/test/square_ink_drop_animation_test_api.h" +#include "ui/views/animation/test/test_ink_drop_animation_observer.h" namespace views { namespace test { -namespace { - -// Transforms a copy of |point| with |transform| and returns it. -gfx::Point TransformPoint(const gfx::Transform& transform, - const gfx::Point& point) { - gfx::Point transformed_point = point; - transform.TransformPoint(&transformed_point); - return transformed_point; -} - -} // namespace +// Represents all the derivatives of the InkDropAnimation class. To be used with +// the InkDropAnimationTest fixture to test all derviatives. +enum InkDropAnimationTestTypes { + SQUARE_INK_DROP_ANIMATION, + FLOOD_FILL_INK_DROP_ANIMATION +}; -class InkDropAnimationTest : public testing::Test { +// Test fixture for all InkDropAnimation class derivatives. +// +// To add a new derivative: +// 1. Add a value to the InkDropAnimationTestTypes enum. +// 2. Implement set up and tear down code for the new enum value in +// InkDropAnimationTest() and +// ~InkDropAnimationTest(). +// 3. Add the new enum value to the INSTANTIATE_TEST_CASE_P) Values list. +class InkDropAnimationTest + : public testing::TestWithParam<InkDropAnimationTestTypes> { public: - InkDropAnimationTest() {} - ~InkDropAnimationTest() override {} + InkDropAnimationTest(); + ~InkDropAnimationTest() override; protected: - scoped_ptr<InkDropAnimation> CreateInkDropAnimation() const; + TestInkDropAnimationObserver observer_; + + scoped_ptr<InkDropAnimation> ink_drop_animation_; + + scoped_ptr<InkDropAnimationTestApi> test_api_; private: DISALLOW_COPY_AND_ASSIGN(InkDropAnimationTest); }; -// Returns a new InkDropAnimation with default parameters. -scoped_ptr<InkDropAnimation> InkDropAnimationTest::CreateInkDropAnimation() - const { - return make_scoped_ptr( - new InkDropAnimation(gfx::Size(10, 10), 2, gfx::Size(8, 8), 1)); +InkDropAnimationTest::InkDropAnimationTest() { + switch (GetParam()) { + case SQUARE_INK_DROP_ANIMATION: { + SquareInkDropAnimation* square_ink_drop_animation = + new SquareInkDropAnimation(gfx::Size(10, 10), 2, gfx::Size(8, 8), 1, + gfx::Point(), SK_ColorBLACK); + ink_drop_animation_.reset(square_ink_drop_animation); + test_api_.reset( + new SquareInkDropAnimationTestApi(square_ink_drop_animation)); + break; + } + case FLOOD_FILL_INK_DROP_ANIMATION: { + FloodFillInkDropAnimation* flood_fill_ink_drop_animation = + new FloodFillInkDropAnimation(gfx::Rect(0, 0, 10, 10), gfx::Point(), + SK_ColorBLACK); + ink_drop_animation_.reset(flood_fill_ink_drop_animation); + test_api_.reset( + new FloodFillInkDropAnimationTestApi(flood_fill_ink_drop_animation)); + break; + } + } + ink_drop_animation_->set_observer(&observer_); + observer_.set_ink_drop_animation(ink_drop_animation_.get()); + test_api_->SetDisableAnimationTimers(true); +} + +InkDropAnimationTest::~InkDropAnimationTest() {} + +// Note: First argument is optional and intentionally left blank. +// (it's a prefix for the generated test cases) +INSTANTIATE_TEST_CASE_P(, + InkDropAnimationTest, + testing::Values(SQUARE_INK_DROP_ANIMATION, + FLOOD_FILL_INK_DROP_ANIMATION)); + +TEST_P(InkDropAnimationTest, InitialStateAfterConstruction) { + EXPECT_EQ(views::InkDropState::HIDDEN, + ink_drop_animation_->target_ink_drop_state()); +} + +// Verify no animations are used when animating from HIDDEN to HIDDEN. +TEST_P(InkDropAnimationTest, AnimateToHiddenFromInvisibleState) { + EXPECT_EQ(InkDropState::HIDDEN, ink_drop_animation_->target_ink_drop_state()); + + ink_drop_animation_->AnimateToState(InkDropState::HIDDEN); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); + EXPECT_FALSE(ink_drop_animation_->IsVisible()); } -TEST_F(InkDropAnimationTest, InitialStateAfterConstruction) { - scoped_ptr<InkDropAnimation> ink_drop_animation = CreateInkDropAnimation(); - EXPECT_EQ(views::InkDropState::HIDDEN, ink_drop_animation->ink_drop_state()); +TEST_P(InkDropAnimationTest, AnimateToHiddenFromVisibleState) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + + EXPECT_NE(InkDropState::HIDDEN, ink_drop_animation_->target_ink_drop_state()); + + ink_drop_animation_->AnimateToState(InkDropState::HIDDEN); + test_api_->CompleteAnimations(); + + EXPECT_EQ(3, observer_.last_animation_started_ordinal()); + EXPECT_EQ(4, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, ActionPendingOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, QuickActionOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_TRIGGERED); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, SlowActionPendingOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState( + views::InkDropState::ALTERNATE_ACTION_PENDING); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, SlowActionOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState( + views::InkDropState::ALTERNATE_ACTION_PENDING); + ink_drop_animation_->AnimateToState( + views::InkDropState::ALTERNATE_ACTION_TRIGGERED); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, ActivatedOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTIVATED); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); } -TEST_F(InkDropAnimationTest, AnimateToActionPending) { - scoped_ptr<InkDropAnimation> ink_drop_animation = CreateInkDropAnimation(); - ink_drop_animation->AnimateToState(views::InkDropState::ACTION_PENDING); +TEST_P(InkDropAnimationTest, DeactivatedOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTIVATED); + ink_drop_animation_->AnimateToState(views::InkDropState::DEACTIVATED); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +// Verify animations are aborted during deletion and the +// InkDropAnimationObservers are notified. +TEST_P(InkDropAnimationTest, AnimationsAbortedDuringDeletion) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_.reset(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); EXPECT_EQ(views::InkDropState::ACTION_PENDING, - ink_drop_animation->ink_drop_state()); + observer_.last_animation_ended_context()); + EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED, + observer_.last_animation_ended_reason()); +} + +TEST_P(InkDropAnimationTest, VerifyObserversAreNotified) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); + + EXPECT_TRUE(test_api_->HasActiveAnimations()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_TRUE(observer_.AnimationHasNotEnded()); + EXPECT_EQ(InkDropState::ACTION_PENDING, + observer_.last_animation_started_context()); + + test_api_->CompleteAnimations(); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropState::ACTION_PENDING, + observer_.last_animation_ended_context()); +} + +TEST_P(InkDropAnimationTest, VerifyObserversAreNotifiedOfSuccessfulAnimations) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimationEndedReason::SUCCESS, + observer_.last_animation_ended_reason()); +} + +TEST_P(InkDropAnimationTest, VerifyObserversAreNotifiedOfPreemptedAnimations) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(InkDropState::ALTERNATE_ACTION_PENDING); + + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED, + observer_.last_animation_ended_reason()); +} + +TEST_P(InkDropAnimationTest, InkDropStatesPersistWhenCallingAnimateToState) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::ACTIVATED); + EXPECT_EQ(views::InkDropState::ACTIVATED, + ink_drop_animation_->target_ink_drop_state()); } -TEST_F(InkDropAnimationTest, AnimateToQuickAction) { - scoped_ptr<InkDropAnimation> ink_drop_animation = CreateInkDropAnimation(); - ink_drop_animation->AnimateToState(views::InkDropState::QUICK_ACTION); - EXPECT_EQ(views::InkDropState::QUICK_ACTION, - ink_drop_animation->ink_drop_state()); +TEST_P(InkDropAnimationTest, HideImmediatelyWithoutActiveAnimations) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_NE(InkDropState::HIDDEN, ink_drop_animation_->target_ink_drop_state()); + + ink_drop_animation_->HideImmediately(); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_EQ(views::InkDropState::HIDDEN, + ink_drop_animation_->target_ink_drop_state()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); + EXPECT_FALSE(ink_drop_animation_->IsVisible()); } -TEST_F(InkDropAnimationTest, AnimateToSlowActionPending) { - scoped_ptr<InkDropAnimation> ink_drop_animation = CreateInkDropAnimation(); - ink_drop_animation->AnimateToState(views::InkDropState::SLOW_ACTION_PENDING); - EXPECT_EQ(views::InkDropState::SLOW_ACTION_PENDING, - ink_drop_animation->ink_drop_state()); +// Verifies all active animations are aborted and the InkDropState is set to +// HIDDEN after invoking HideImmediately(). +TEST_P(InkDropAnimationTest, HideImmediatelyWithActiveAnimations) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + EXPECT_TRUE(test_api_->HasActiveAnimations()); + EXPECT_NE(InkDropState::HIDDEN, ink_drop_animation_->target_ink_drop_state()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + + ink_drop_animation_->HideImmediately(); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_EQ(views::InkDropState::HIDDEN, + ink_drop_animation_->target_ink_drop_state()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropState::ACTION_PENDING, + observer_.last_animation_ended_context()); + EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED, + observer_.last_animation_ended_reason()); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); + EXPECT_FALSE(ink_drop_animation_->IsVisible()); } -TEST_F(InkDropAnimationTest, AnimateToSlowAction) { - scoped_ptr<InkDropAnimation> ink_drop_animation = CreateInkDropAnimation(); - ink_drop_animation->AnimateToState(views::InkDropState::SLOW_ACTION); - EXPECT_EQ(views::InkDropState::SLOW_ACTION, - ink_drop_animation->ink_drop_state()); +TEST_P(InkDropAnimationTest, SnapToActivatedWithoutActiveAnimations) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_NE(InkDropState::ACTIVATED, + ink_drop_animation_->target_ink_drop_state()); + + ink_drop_animation_->SnapToActivated(); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_EQ(views::InkDropState::ACTIVATED, + ink_drop_animation_->target_ink_drop_state()); + EXPECT_EQ(3, observer_.last_animation_started_ordinal()); + EXPECT_EQ(4, observer_.last_animation_ended_ordinal()); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); + EXPECT_TRUE(ink_drop_animation_->IsVisible()); } -TEST_F(InkDropAnimationTest, AnimateToActivated) { - scoped_ptr<InkDropAnimation> ink_drop_animation = CreateInkDropAnimation(); - ink_drop_animation->AnimateToState(views::InkDropState::ACTIVATED); +// Verifies all active animations are aborted and the InkDropState is set to +// ACTIVATED after invoking SnapToActivated(). +TEST_P(InkDropAnimationTest, SnapToActivatedWithActiveAnimations) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + EXPECT_TRUE(test_api_->HasActiveAnimations()); + EXPECT_NE(InkDropState::ACTIVATED, + ink_drop_animation_->target_ink_drop_state()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + + ink_drop_animation_->SnapToActivated(); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); EXPECT_EQ(views::InkDropState::ACTIVATED, - ink_drop_animation->ink_drop_state()); + ink_drop_animation_->target_ink_drop_state()); + EXPECT_EQ(3, observer_.last_animation_started_ordinal()); + EXPECT_EQ(4, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropState::ACTIVATED, observer_.last_animation_ended_context()); + EXPECT_EQ(InkDropAnimationEndedReason::SUCCESS, + observer_.last_animation_ended_reason()); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); + EXPECT_TRUE(ink_drop_animation_->IsVisible()); } -TEST_F(InkDropAnimationTest, AnimateToDeactivated) { - scoped_ptr<InkDropAnimation> ink_drop_animation = CreateInkDropAnimation(); - ink_drop_animation->AnimateToState(views::InkDropState::DEACTIVATED); - EXPECT_EQ(views::InkDropState::DEACTIVATED, - ink_drop_animation->ink_drop_state()); +TEST_P(InkDropAnimationTest, AnimateToVisibleFromHidden) { + EXPECT_EQ(InkDropState::HIDDEN, ink_drop_animation_->target_ink_drop_state()); + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + EXPECT_TRUE(ink_drop_animation_->IsVisible()); } -TEST_F(InkDropAnimationTest, - TransformedPointsUsingTransformsFromCalculateCircleTransforms) { - const int kHalfDrawnSize = 5; - const int kDrawnSize = 2 * kHalfDrawnSize; - - const int kHalfTransformedSize = 100; - const int kTransformedSize = 2 * kHalfTransformedSize; - - // Constant points in the drawn space that will be transformed. - const gfx::Point center(kHalfDrawnSize, kHalfDrawnSize); - const gfx::Point mid_left(0, kHalfDrawnSize); - const gfx::Point mid_right(kDrawnSize, kHalfDrawnSize); - const gfx::Point top_mid(kHalfDrawnSize, 0); - const gfx::Point bottom_mid(kHalfDrawnSize, kDrawnSize); - - scoped_ptr<InkDropAnimation> ink_drop_animation( - new InkDropAnimation(gfx::Size(kDrawnSize, kDrawnSize), 2, - gfx::Size(kHalfDrawnSize, kHalfDrawnSize), 1)); - InkDropAnimationTestApi test_api(ink_drop_animation.get()); - - InkDropAnimationTestApi::InkDropTransforms transforms; - test_api.CalculateCircleTransforms( - gfx::Size(kTransformedSize, kTransformedSize), &transforms); - - // Transform variables to reduce verbosity of actual verification code. - const gfx::Transform kTopLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_LEFT_CIRCLE]; - const gfx::Transform kTopRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_RIGHT_CIRCLE]; - const gfx::Transform kBottomRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_RIGHT_CIRCLE]; - const gfx::Transform kBottomLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_LEFT_CIRCLE]; - const gfx::Transform kHorizontalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::HORIZONTAL_RECT]; - const gfx::Transform kVerticalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::VERTICAL_RECT]; - - // Top left circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kTopLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kTopLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kTopLeftTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kTopLeftTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kTopLeftTransform, bottom_mid)); - - // Top right circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kTopRightTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kTopRightTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kTopRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kTopRightTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kTopRightTransform, bottom_mid)); - - // Bottom right circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kBottomRightTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kBottomRightTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kBottomRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kBottomRightTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kBottomRightTransform, bottom_mid)); - - // Bottom left circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kBottomLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kBottomLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kBottomLeftTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kBottomLeftTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kBottomLeftTransform, bottom_mid)); - - // Horizontal rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kHorizontalTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kHorizontalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, bottom_mid)); - - // Vertical rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, center)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, mid_left)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kVerticalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kVerticalTransform, bottom_mid)); +// Verifies that the value of InkDropAnimation::target_ink_drop_state() returns +// the most recent value passed to AnimateToState() when notifying observers +// that an animation has started within the AnimateToState() function call. +TEST_P(InkDropAnimationTest, TargetInkDropStateOnAnimationStarted) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN); + + EXPECT_EQ(3, observer_.last_animation_started_ordinal()); + EXPECT_EQ(views::InkDropState::HIDDEN, + observer_.target_state_at_last_animation_started()); } -TEST_F(InkDropAnimationTest, - TransformedPointsUsingTransformsFromCalculateRectTransforms) { - const int kHalfDrawnSize = 5; - const int kDrawnSize = 2 * kHalfDrawnSize; - - const int kTransformedRadius = 10; - - const int kHalfTransformedWidth = 100; - const int kTransformedWidth = 2 * kHalfTransformedWidth; - - const int kHalfTransformedHeight = 100; - const int kTransformedHeight = 2 * kHalfTransformedHeight; - - // Constant points in the drawn space that will be transformed. - const gfx::Point center(kHalfDrawnSize, kHalfDrawnSize); - const gfx::Point mid_left(0, kHalfDrawnSize); - const gfx::Point mid_right(kDrawnSize, kHalfDrawnSize); - const gfx::Point top_mid(kHalfDrawnSize, 0); - const gfx::Point bottom_mid(kHalfDrawnSize, kDrawnSize); - - scoped_ptr<InkDropAnimation> ink_drop_animation( - new InkDropAnimation(gfx::Size(kDrawnSize, kDrawnSize), 2, - gfx::Size(kHalfDrawnSize, kHalfDrawnSize), 1)); - InkDropAnimationTestApi test_api(ink_drop_animation.get()); - - InkDropAnimationTestApi::InkDropTransforms transforms; - test_api.CalculateRectTransforms( - gfx::Size(kTransformedWidth, kTransformedHeight), kTransformedRadius, - &transforms); - - // Transform variables to reduce verbosity of actual verification code. - const gfx::Transform kTopLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_LEFT_CIRCLE]; - const gfx::Transform kTopRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_RIGHT_CIRCLE]; - const gfx::Transform kBottomRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_RIGHT_CIRCLE]; - const gfx::Transform kBottomLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_LEFT_CIRCLE]; - const gfx::Transform kHorizontalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::HORIZONTAL_RECT]; - const gfx::Transform kVerticalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::VERTICAL_RECT]; - - const int x_offset = kHalfTransformedWidth - kTransformedRadius; - const int y_offset = kHalfTransformedWidth - kTransformedRadius; - - // Top left circle - EXPECT_EQ(gfx::Point(-x_offset, -y_offset), - TransformPoint(kTopLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedWidth, -y_offset), - TransformPoint(kTopLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(-x_offset, -kHalfTransformedHeight), - TransformPoint(kTopLeftTransform, top_mid)); - - // Top right circle - EXPECT_EQ(gfx::Point(x_offset, -y_offset), - TransformPoint(kTopRightTransform, center)); - EXPECT_EQ(gfx::Point(kHalfTransformedWidth, -y_offset), - TransformPoint(kTopRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(x_offset, -kHalfTransformedHeight), - TransformPoint(kTopRightTransform, top_mid)); - - // Bottom right circle - EXPECT_EQ(gfx::Point(x_offset, y_offset), - TransformPoint(kBottomRightTransform, center)); - EXPECT_EQ(gfx::Point(kHalfTransformedWidth, y_offset), - TransformPoint(kBottomRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(x_offset, kHalfTransformedHeight), - TransformPoint(kBottomRightTransform, bottom_mid)); - - // Bottom left circle - EXPECT_EQ(gfx::Point(-x_offset, y_offset), - TransformPoint(kBottomLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedWidth, y_offset), - TransformPoint(kBottomLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(-x_offset, kHalfTransformedHeight), - TransformPoint(kBottomLeftTransform, bottom_mid)); - - // Horizontal rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedWidth, 0), - TransformPoint(kHorizontalTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedWidth, 0), - TransformPoint(kHorizontalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -y_offset), - TransformPoint(kHorizontalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, y_offset), - TransformPoint(kHorizontalTransform, bottom_mid)); - - // Vertical rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, center)); - EXPECT_EQ(gfx::Point(-x_offset, 0), - TransformPoint(kVerticalTransform, mid_left)); - EXPECT_EQ(gfx::Point(x_offset, 0), - TransformPoint(kVerticalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedHeight), - TransformPoint(kVerticalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedHeight), - TransformPoint(kVerticalTransform, bottom_mid)); +// Verifies that the value of InkDropAnimation::target_ink_drop_state() returns +// the most recent value passed to AnimateToState() when notifying observers +// that an animation has ended within the AnimateToState() function call. +TEST_P(InkDropAnimationTest, TargetInkDropStateOnAnimationEnded) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN); + + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(views::InkDropState::HIDDEN, + observer_.target_state_at_last_animation_ended()); } } // namespace test diff --git a/chromium/ui/views/animation/ink_drop_delegate.h b/chromium/ui/views/animation/ink_drop_delegate.h index 576b742a610..61235241f92 100644 --- a/chromium/ui/views/animation/ink_drop_delegate.h +++ b/chromium/ui/views/animation/ink_drop_delegate.h @@ -23,25 +23,18 @@ class VIEWS_EXPORT InkDropDelegate { InkDropDelegate() {} virtual ~InkDropDelegate() {} - // Sets sizes for the animation layers that are squares with |large_size| and - // |small_size| being the length of each side. When painting rounded squares - // |large_corner_radius| and |small_corner_radius| are specifying the - // corner radius. - virtual void SetInkDropSize(int large_size, - int large_corner_radius, - int small_size, - int small_corner_radius) = 0; - - // Called when the bounds or layout of the View changes necessitating change - // in positioning of ink ripple layers. - virtual void OnLayout() = 0; - // Called when ink ripple state changes. // TODO(bruthig): Replace the InkDropState parameter with an InkDropAction // enum. The InkDropAction enum should be a subset of the InkDropState values // as well as a NONE value. virtual void OnAction(InkDropState state) = 0; + // Immediately snaps the InkDropState to ACTIVATED. + virtual void SnapToActivated() = 0; + + // Enables or disables the hover state. + virtual void SetHovered(bool is_hovered) = 0; + private: DISALLOW_COPY_AND_ASSIGN(InkDropDelegate); }; diff --git a/chromium/ui/views/animation/ink_drop_host.h b/chromium/ui/views/animation/ink_drop_host.h index f50fac789d7..570f2710b49 100644 --- a/chromium/ui/views/animation/ink_drop_host.h +++ b/chromium/ui/views/animation/ink_drop_host.h @@ -6,6 +6,7 @@ #define UI_VIEWS_ANIMATION_INK_DROP_HOST_H_ #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "ui/gfx/geometry/point.h" #include "ui/views/views_export.h" @@ -15,6 +16,9 @@ class Layer; namespace views { +class InkDropAnimation; +class InkDropHover; + // Used by the InkDropAnimationController to add and remove the ink drop layers // from a host's layer tree. Typically the ink drop layer is added to a View's // layer but it can also be added to a View's ancestor layer. @@ -33,9 +37,11 @@ class VIEWS_EXPORT InkDropHost { // Removes |ink_drop_layer| from the layer tree. virtual void RemoveInkDropLayer(ui::Layer* ink_drop_layer) = 0; - // Returns the Point where the ink drop should be centered. - // TODO(varkha): This should be moved to InkDropConsumer. - virtual gfx::Point CalculateInkDropCenter() const = 0; + // Creates and returns the effect used for press. + virtual scoped_ptr<InkDropAnimation> CreateInkDropAnimation() const = 0; + + // Creates and returns the effect used for hover. + virtual scoped_ptr<InkDropHover> CreateInkDropHover() const = 0; private: DISALLOW_COPY_AND_ASSIGN(InkDropHost); diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc new file mode 100644 index 00000000000..317645e6118 --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_host_view.cc @@ -0,0 +1,74 @@ +// 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/views/animation/ink_drop_host_view.h" + +#include "ui/gfx/color_palette.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/views/animation/ink_drop_hover.h" +#include "ui/views/animation/square_ink_drop_animation.h" + +namespace views { + +// Default sizes for ink drop effects. +const int kInkDropSize = 24; +const int kInkDropLargeCornerRadius = 4; + +// The scale factor to compute the large ink drop size. +const float kLargeInkDropScale = 1.333f; + +namespace { + +gfx::Size CalculateLargeInkDropSize(const gfx::Size small_size) { + return gfx::ScaleToCeiledSize(gfx::Size(small_size), kLargeInkDropScale); +} + +} // namespace + +// static +const int InkDropHostView::kInkDropSmallCornerRadius = 2; + +InkDropHostView::InkDropHostView() + : ink_drop_size_(kInkDropSize, kInkDropSize) {} + +InkDropHostView::~InkDropHostView() {} + +void InkDropHostView::AddInkDropLayer(ui::Layer* ink_drop_layer) { + SetPaintToLayer(true); + layer()->SetFillsBoundsOpaquely(false); + layer()->Add(ink_drop_layer); + layer()->StackAtBottom(ink_drop_layer); +} + +void InkDropHostView::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { + layer()->Remove(ink_drop_layer); + SetPaintToLayer(false); +} + +scoped_ptr<InkDropAnimation> InkDropHostView::CreateInkDropAnimation() const { + scoped_ptr<InkDropAnimation> animation(new SquareInkDropAnimation( + CalculateLargeInkDropSize(ink_drop_size_), kInkDropLargeCornerRadius, + ink_drop_size_, kInkDropSmallCornerRadius, GetInkDropCenter(), + GetInkDropBaseColor())); + return animation; +} + +scoped_ptr<InkDropHover> InkDropHostView::CreateInkDropHover() const { + scoped_ptr<InkDropHover> hover( + new InkDropHover(ink_drop_size_, kInkDropSmallCornerRadius, + GetInkDropCenter(), GetInkDropBaseColor())); + hover->set_explode_size(CalculateLargeInkDropSize(ink_drop_size_)); + return hover; +} + +gfx::Point InkDropHostView::GetInkDropCenter() const { + return GetLocalBounds().CenterPoint(); +} + +SkColor InkDropHostView::GetInkDropBaseColor() const { + NOTREACHED(); + return gfx::kPlaceholderColor; +} + +} // namespace views diff --git a/chromium/ui/views/animation/ink_drop_host_view.h b/chromium/ui/views/animation/ink_drop_host_view.h new file mode 100644 index 00000000000..3a3efa6b6ed --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_host_view.h @@ -0,0 +1,48 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_HOST_VIEW_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_HOST_VIEW_H_ + +#include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/animation/ink_drop_host.h" +#include "ui/views/view.h" + +namespace views { + +class InkDropAnimation; +class InkDropHover; + +// A view that provides InkDropHost functionality. +class VIEWS_EXPORT InkDropHostView : public views::View, public InkDropHost { + public: + InkDropHostView(); + ~InkDropHostView() override; + + // Overridden from views::InkDropHost: + void AddInkDropLayer(ui::Layer* ink_drop_layer) override; + void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; + scoped_ptr<InkDropAnimation> CreateInkDropAnimation() const override; + scoped_ptr<InkDropHover> CreateInkDropHover() const override; + + void set_ink_drop_size(const gfx::Size& size) { ink_drop_size_ = size; } + + protected: + static const int kInkDropSmallCornerRadius; + + // Overrideable methods to allow views to provide minor tweaks to the default + // ink drop. + virtual gfx::Point GetInkDropCenter() const; + virtual SkColor GetInkDropBaseColor() const; + + private: + gfx::Size ink_drop_size_; + + DISALLOW_COPY_AND_ASSIGN(InkDropHostView); +}; +} + +#endif // UI_VIEWS_ANIMATION_INK_DROP_HOST_VIEW_H_ diff --git a/chromium/ui/views/animation/ink_drop_hover.cc b/chromium/ui/views/animation/ink_drop_hover.cc new file mode 100644 index 00000000000..af98e614f59 --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_hover.cc @@ -0,0 +1,146 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/animation/ink_drop_hover.h" + +#include "third_party/skia/include/core/SkColor.h" +#include "ui/compositor/callback_layer_animation_observer.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animation_sequence.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/views/animation/ink_drop_hover_observer.h" +#include "ui/views/animation/ink_drop_painted_layer_delegates.h" + +namespace views { + +namespace { + +// The opacity of the hover when it is visible. +const float kHoverVisibleOpacity = 0.128f; + +// The opacity of the hover when it is not visible. +const float kHiddenOpacity = 0.0f; + +} // namespace + +InkDropHover::InkDropHover(const gfx::Size& size, + int corner_radius, + const gfx::Point& center_point, + SkColor color) + : size_(size), + explode_size_(size), + center_point_(center_point), + last_animation_initiated_was_fade_in_(false), + layer_delegate_( + new RoundedRectangleLayerDelegate(color, size, corner_radius)), + layer_(new ui::Layer()), + observer_(nullptr) { + layer_->SetBounds(gfx::Rect(size)); + layer_->SetFillsBoundsOpaquely(false); + layer_->set_delegate(layer_delegate_.get()); + layer_->SetVisible(false); + layer_->SetOpacity(kHoverVisibleOpacity); + layer_->SetMasksToBounds(false); + layer_->set_name("InkDropHover:layer"); +} + +InkDropHover::~InkDropHover() {} + +bool InkDropHover::IsFadingInOrVisible() const { + return last_animation_initiated_was_fade_in_; +} + +void InkDropHover::FadeIn(const base::TimeDelta& duration) { + layer_->SetOpacity(kHiddenOpacity); + layer_->SetVisible(true); + AnimateFade(FADE_IN, duration, size_, size_); +} + +void InkDropHover::FadeOut(const base::TimeDelta& duration, bool explode) { + AnimateFade(FADE_OUT, duration, size_, explode ? explode_size_ : size_); +} + +test::InkDropHoverTestApi* InkDropHover::GetTestApi() { + return nullptr; +} + +void InkDropHover::AnimateFade(AnimationType animation_type, + const base::TimeDelta& duration, + const gfx::Size& initial_size, + const gfx::Size& target_size) { + last_animation_initiated_was_fade_in_ = animation_type == FADE_IN; + + layer_->SetTransform(CalculateTransform(initial_size)); + + // The |animation_observer| will be destroyed when the + // AnimationStartedCallback() returns true. + ui::CallbackLayerAnimationObserver* animation_observer = + new ui::CallbackLayerAnimationObserver( + base::Bind(&InkDropHover::AnimationStartedCallback, + base::Unretained(this), animation_type), + base::Bind(&InkDropHover::AnimationEndedCallback, + base::Unretained(this), animation_type)); + + ui::LayerAnimator* animator = layer_->GetAnimator(); + ui::ScopedLayerAnimationSettings animation(animator); + animation.SetTweenType(gfx::Tween::EASE_IN_OUT); + animation.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + + ui::LayerAnimationElement* opacity_element = + ui::LayerAnimationElement::CreateOpacityElement( + animation_type == FADE_IN ? kHoverVisibleOpacity : kHiddenOpacity, + duration); + ui::LayerAnimationSequence* opacity_sequence = + new ui::LayerAnimationSequence(opacity_element); + opacity_sequence->AddObserver(animation_observer); + animator->StartAnimation(opacity_sequence); + + if (initial_size != target_size) { + ui::LayerAnimationElement* transform_element = + ui::LayerAnimationElement::CreateTransformElement( + CalculateTransform(target_size), duration); + ui::LayerAnimationSequence* transform_sequence = + new ui::LayerAnimationSequence(transform_element); + transform_sequence->AddObserver(animation_observer); + animator->StartAnimation(transform_sequence); + } + + animation_observer->SetActive(); +} + +gfx::Transform InkDropHover::CalculateTransform(const gfx::Size& size) const { + gfx::Transform transform; + transform.Translate(center_point_.x(), center_point_.y()); + transform.Scale(size.width() / size_.width(), size.height() / size_.height()); + transform.Translate(-layer_delegate_->GetCenterPoint().x(), + -layer_delegate_->GetCenterPoint().y()); + return transform; +} + +void InkDropHover::AnimationStartedCallback( + AnimationType animation_type, + const ui::CallbackLayerAnimationObserver& observer) { + if (observer_) + observer_->AnimationStarted(animation_type); +} + +bool InkDropHover::AnimationEndedCallback( + AnimationType animation_type, + const ui::CallbackLayerAnimationObserver& observer) { + // AnimationEndedCallback() may be invoked when this is being destroyed and + // |layer_| may be null. + if (animation_type == FADE_OUT && layer_) + layer_->SetVisible(false); + + if (observer_) { + observer_->AnimationEnded(animation_type, + observer.aborted_count() + ? InkDropAnimationEndedReason::PRE_EMPTED + : InkDropAnimationEndedReason::SUCCESS); + } + return true; +} + +} // namespace views diff --git a/chromium/ui/views/animation/ink_drop_hover.h b/chromium/ui/views/animation/ink_drop_hover.h new file mode 100644 index 00000000000..e1cd2d42e5e --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_hover.h @@ -0,0 +1,117 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_HOVER_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_HOVER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/transform.h" +#include "ui/views/views_export.h" + +namespace ui { +class Layer; +class CallbackLayerAnimationObserver; +} // namespace ui + +namespace views { +namespace test { +class InkDropHoverTestApi; +} // namespace test + +class RoundedRectangleLayerDelegate; +class InkDropHoverObserver; + +// Manages fade in/out animations for a painted Layer that is used to provide +// visual feedback on ui::Views for mouse hover states. +class VIEWS_EXPORT InkDropHover { + public: + enum AnimationType { FADE_IN, FADE_OUT }; + + InkDropHover(const gfx::Size& size, + int corner_radius, + const gfx::Point& center_point, + SkColor color); + virtual ~InkDropHover(); + + void set_observer(InkDropHoverObserver* observer) { observer_ = observer; } + + void set_explode_size(const gfx::Size& size) { explode_size_ = size; } + + // Returns true if the hover animation is either in the process of fading + // in or is fully visible. + bool IsFadingInOrVisible() const; + + // Fades in the hover visual over the given |duration|. + void FadeIn(const base::TimeDelta& duration); + + // Fades out the hover visual over the given |duration|. If |explode| is true + // then the hover will animate a size increase in addition to the fade out. + void FadeOut(const base::TimeDelta& duration, bool explode); + + // The root Layer that can be added in to a Layer tree. + ui::Layer* layer() { return layer_.get(); } + + // Returns a test api to access internals of this. Default implmentations + // should return nullptr and test specific subclasses can override to return + // an instance. + virtual test::InkDropHoverTestApi* GetTestApi(); + + private: + friend class test::InkDropHoverTestApi; + + // Animates a fade in/out as specified by |animation_type| combined with a + // transformation from the |initial_size| to the |target_size| over the given + // |duration|. + void AnimateFade(AnimationType animation_type, + const base::TimeDelta& duration, + const gfx::Size& initial_size, + const gfx::Size& target_size); + + // Calculates the Transform to apply to |layer_| for the given |size|. + gfx::Transform CalculateTransform(const gfx::Size& size) const; + + // The callback that will be invoked when a fade in/out animation is started. + void AnimationStartedCallback( + AnimationType animation_type, + const ui::CallbackLayerAnimationObserver& observer); + + // The callback that will be invoked when a fade in/out animation is complete. + bool AnimationEndedCallback( + AnimationType animation_type, + const ui::CallbackLayerAnimationObserver& observer); + + // The size of the hover shape when fully faded in. + gfx::Size size_; + + // The target size of the hover shape when it expands during a fade out + // animation. + gfx::Size explode_size_; + + // The center point of the hover shape in the parent Layer's coordinate space. + gfx::PointF center_point_; + + // True if the last animation to be initiated was a FADE_IN, and false + // otherwise. + bool last_animation_initiated_was_fade_in_; + + // The LayerDelegate that paints the hover |layer_|. + scoped_ptr<RoundedRectangleLayerDelegate> layer_delegate_; + + // The visual hover layer that is painted by |layer_delegate_|. + scoped_ptr<ui::Layer> layer_; + + InkDropHoverObserver* observer_; + + DISALLOW_COPY_AND_ASSIGN(InkDropHover); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_HOVER_H_ diff --git a/chromium/ui/views/animation/ink_drop_hover_observer.h b/chromium/ui/views/animation/ink_drop_hover_observer.h new file mode 100644 index 00000000000..4e31bc7a428 --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_hover_observer.h @@ -0,0 +1,41 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_HOVER_OBSERVER_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_HOVER_OBSERVER_H_ + +#include <string> + +#include "base/macros.h" +#include "ui/views/animation/ink_drop_animation_ended_reason.h" +#include "ui/views/animation/ink_drop_hover.h" +#include "ui/views/views_export.h" + +namespace views { + +// Observer to attach to an InkDropHover animation. +class VIEWS_EXPORT InkDropHoverObserver { + public: + // An animation for the given |animation_type| has started. + virtual void AnimationStarted(InkDropHover::AnimationType animation_type) = 0; + + // Notifies the observer that an animation for the given |animation_type| has + // finished and the reason for completion is given by |reason|. If |reason| is + // SUCCESS then the animation has progressed to its final frame however if + // |reason| is |PRE_EMPTED| then the animation was stopped before its final + // frame. + virtual void AnimationEnded(InkDropHover::AnimationType animation_type, + InkDropAnimationEndedReason reason) = 0; + + protected: + InkDropHoverObserver() = default; + virtual ~InkDropHoverObserver() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(InkDropHoverObserver); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_HOVER_OBSERVER_H_ diff --git a/chromium/ui/views/animation/ink_drop_hover_unittest.cc b/chromium/ui/views/animation/ink_drop_hover_unittest.cc new file mode 100644 index 00000000000..5d02ab7f1ed --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_hover_unittest.cc @@ -0,0 +1,144 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/animation/ink_drop_hover.h" + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/animation/test/ink_drop_hover_test_api.h" +#include "ui/views/animation/test/test_ink_drop_hover_observer.h" + +namespace views { +namespace test { + +class InkDropHoverTest : public testing::Test { + public: + InkDropHoverTest(); + ~InkDropHoverTest() override; + + protected: + // The test target. + scoped_ptr<InkDropHover> ink_drop_hover_; + + // Allows privileged access to the the |ink_drop_hover_|. + InkDropHoverTestApi test_api_; + + // Observer of the test target. + TestInkDropHoverObserver observer_; + + private: + DISALLOW_COPY_AND_ASSIGN(InkDropHoverTest); +}; + +InkDropHoverTest::InkDropHoverTest() + : ink_drop_hover_( + new InkDropHover(gfx::Size(10, 10), 3, gfx::Point(), SK_ColorBLACK)), + test_api_(ink_drop_hover_.get()) { + ink_drop_hover_->set_observer(&observer_); + + test_api_.SetDisableAnimationTimers(true); +} + +InkDropHoverTest::~InkDropHoverTest() {} + +TEST_F(InkDropHoverTest, InitialStateAfterConstruction) { + EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible()); +} + +TEST_F(InkDropHoverTest, IsHoveredStateTransitions) { + ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1)); + EXPECT_TRUE(ink_drop_hover_->IsFadingInOrVisible()); + + test_api_.CompleteAnimations(); + EXPECT_TRUE(ink_drop_hover_->IsFadingInOrVisible()); + + ink_drop_hover_->FadeOut(base::TimeDelta::FromSeconds(1), + false /* explode */); + EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible()); + + test_api_.CompleteAnimations(); + EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible()); +} + +TEST_F(InkDropHoverTest, VerifyObserversAreNotified) { + ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1)); + + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_FALSE(observer_.AnimationHasEnded()); + + test_api_.CompleteAnimations(); + + EXPECT_TRUE(observer_.AnimationHasEnded()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); +} + +TEST_F(InkDropHoverTest, VerifyObserversAreNotifiedWithCorrectAnimationType) { + ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1)); + + EXPECT_TRUE(observer_.AnimationHasStarted()); + EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_started_context()); + + test_api_.CompleteAnimations(); + EXPECT_TRUE(observer_.AnimationHasEnded()); + EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_started_context()); + + ink_drop_hover_->FadeOut(base::TimeDelta::FromSeconds(1), + false /* explode */); + EXPECT_EQ(InkDropHover::FADE_OUT, observer_.last_animation_started_context()); + + test_api_.CompleteAnimations(); + EXPECT_EQ(InkDropHover::FADE_OUT, observer_.last_animation_started_context()); +} + +TEST_F(InkDropHoverTest, VerifyObserversAreNotifiedOfSuccessfulAnimations) { + ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1)); + test_api_.CompleteAnimations(); + + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimationEndedReason::SUCCESS, + observer_.last_animation_ended_reason()); +} + +TEST_F(InkDropHoverTest, VerifyObserversAreNotifiedOfPreemptedAnimations) { + ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1)); + ink_drop_hover_->FadeOut(base::TimeDelta::FromSeconds(1), + false /* explode */); + + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_ended_context()); + EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED, + observer_.last_animation_ended_reason()); +} + +// Confirms there is no crash. +TEST_F(InkDropHoverTest, NullObserverIsSafe) { + ink_drop_hover_->set_observer(nullptr); + + ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1)); + test_api_.CompleteAnimations(); + + ink_drop_hover_->FadeOut(base::TimeDelta::FromMilliseconds(0), + false /* explode */); + test_api_.CompleteAnimations(); + EXPECT_FALSE(ink_drop_hover_->IsFadingInOrVisible()); +} + +// Verify animations are aborted during deletion and the InkDropHoverObservers +// are notified. +TEST_F(InkDropHoverTest, AnimationsAbortedDuringDeletion) { + ink_drop_hover_->FadeIn(base::TimeDelta::FromSeconds(1)); + ink_drop_hover_.reset(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropHover::FADE_IN, observer_.last_animation_ended_context()); + EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED, + observer_.last_animation_ended_reason()); +} + +} // namespace test +} // namespace views diff --git a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc index bdb79460e49..cb5f7c57053 100644 --- a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc +++ b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.cc @@ -8,6 +8,7 @@ #include "ui/compositor/paint_recorder.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/rect_f.h" namespace views { @@ -51,7 +52,7 @@ void CircleLayerDelegate::OnPaintLayer(const ui::PaintContext& context) { paint.setFlags(SkPaint::kAntiAlias_Flag); paint.setStyle(SkPaint::kFill_Style); - ui::PaintRecorder recorder(context, gfx::Size(radius_, radius_)); + ui::PaintRecorder recorder(context, gfx::Size(2 * radius_, 2 * radius_)); gfx::Canvas* canvas = recorder.canvas(); canvas->DrawCircle(ToRoundedPoint(GetCenterPoint()), radius_, paint); @@ -82,4 +83,34 @@ void RectangleLayerDelegate::OnPaintLayer(const ui::PaintContext& context) { canvas->DrawRect(gfx::Rect(size_), paint); } +//////////////////////////////////////////////////////////////////////////////// +// +// RoundedRectangleLayerDelegate +// + +RoundedRectangleLayerDelegate::RoundedRectangleLayerDelegate(SkColor color, + gfx::Size size, + int corner_radius) + : BasePaintedLayerDelegate(color), + size_(size), + corner_radius_(corner_radius) {} + +RoundedRectangleLayerDelegate::~RoundedRectangleLayerDelegate() {} + +gfx::PointF RoundedRectangleLayerDelegate::GetCenterPoint() const { + return gfx::RectF(gfx::SizeF(size_)).CenterPoint(); +} + +void RoundedRectangleLayerDelegate::OnPaintLayer( + const ui::PaintContext& context) { + SkPaint paint; + paint.setColor(color()); + paint.setFlags(SkPaint::kAntiAlias_Flag); + paint.setStyle(SkPaint::kFill_Style); + + ui::PaintRecorder recorder(context, size_); + gfx::Canvas* canvas = recorder.canvas(); + canvas->DrawRoundRect(gfx::Rect(size_), corner_radius_, paint); +} + } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h index feb2921b8a6..59e170495e7 100644 --- a/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h +++ b/chromium/ui/views/animation/ink_drop_painted_layer_delegates.h @@ -81,6 +81,31 @@ class RectangleLayerDelegate : public BasePaintedLayerDelegate { DISALLOW_COPY_AND_ASSIGN(RectangleLayerDelegate); }; +// A BasePaintedLayerDelegate that paints a rounded rectangle of a specified +// color, size and corner radius. +class RoundedRectangleLayerDelegate : public BasePaintedLayerDelegate { + public: + RoundedRectangleLayerDelegate(SkColor color, + gfx::Size size, + int corner_radius); + ~RoundedRectangleLayerDelegate() override; + + const gfx::Size& size() const { return size_; } + + // BasePaintedLayerDelegate: + gfx::PointF GetCenterPoint() const override; + void OnPaintLayer(const ui::PaintContext& context) override; + + private: + // The size of the rectangle. + gfx::Size size_; + + // The radius of the corners. + int corner_radius_; + + DISALLOW_COPY_AND_ASSIGN(RoundedRectangleLayerDelegate); +}; + } // namespace views #endif // UI_VIEWS_ANIMATION_INK_DROP_PAINTED_LAYER_DELEGATES_H_ diff --git a/chromium/ui/views/animation/ink_drop_state.cc b/chromium/ui/views/animation/ink_drop_state.cc index e8ea3806425..adbefae1a22 100644 --- a/chromium/ui/views/animation/ink_drop_state.cc +++ b/chromium/ui/views/animation/ink_drop_state.cc @@ -16,12 +16,12 @@ std::string ToString(InkDropState state) { return std::string("HIDDEN"); case InkDropState::ACTION_PENDING: return std::string("ACTION_PENDING"); - case InkDropState::QUICK_ACTION: - return std::string("QUICK_ACTION"); - case InkDropState::SLOW_ACTION_PENDING: - return std::string("SLOW_ACTION_PENDING"); - case InkDropState::SLOW_ACTION: - return std::string("SLOW_ACTION"); + case InkDropState::ACTION_TRIGGERED: + return std::string("ACTION_TRIGGERED"); + case InkDropState::ALTERNATE_ACTION_PENDING: + return std::string("ALTERNATE_ACTION_PENDING"); + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + return std::string("ALTERNATE_ACTION_TRIGGERED"); case InkDropState::ACTIVATED: return std::string("ACTIVATED"); case InkDropState::DEACTIVATED: diff --git a/chromium/ui/views/animation/ink_drop_state.h b/chromium/ui/views/animation/ink_drop_state.h index 16f4fc2bee6..2456455c5da 100644 --- a/chromium/ui/views/animation/ink_drop_state.h +++ b/chromium/ui/views/animation/ink_drop_state.h @@ -16,21 +16,21 @@ enum class InkDropState { // The ink drop is not visible. HIDDEN, // The view is being interacted with but the action to be triggered has not - // yet been determined. + // yet been determined, e.g. a mouse button down. ACTION_PENDING, - // The quick action for the view has been triggered. e.g. a tap gesture to - // click a button. - QUICK_ACTION, - // A view is being interacted with and the pending action will be a 'slow' - // action. e.g. a long press that is still active before releasing. - SLOW_ACTION_PENDING, - // The slow action for the view has been triggered. e.g. a long press release - // to bring up a menu. - SLOW_ACTION, + // The quick action for the view has been triggered, e.g. a tap gesture or a + // mouse click on a button. + ACTION_TRIGGERED, + // A view is being interacted with and the pending action will be a secondary + // action, e.g. a long press. + ALTERNATE_ACTION_PENDING, + // The alternate action for the view has been triggered, e.g. a long press + // release to bring up a menu. + ALTERNATE_ACTION_TRIGGERED, // An active state for a view that is not currently being interacted with. // e.g. a pressed button that is showing a menu. ACTIVATED, - // A previously active state has been toggled to inactive. e.g. a drop down + // A previously active state has been toggled to inactive, e.g. a drop down // menu is closed. DEACTIVATED, }; diff --git a/chromium/ui/views/animation/square_ink_drop_animation.cc b/chromium/ui/views/animation/square_ink_drop_animation.cc new file mode 100644 index 00000000000..58320aa8246 --- /dev/null +++ b/chromium/ui/views/animation/square_ink_drop_animation.cc @@ -0,0 +1,555 @@ +// 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/views/animation/square_ink_drop_animation.h" + +#include <algorithm> + +#include "base/logging.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animation_sequence.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/geometry/point3_f.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector3d_f.h" +#include "ui/gfx/transform_util.h" +#include "ui/views/animation/ink_drop_painted_layer_delegates.h" +#include "ui/views/view.h" + +namespace { + +// The minimum scale factor to use when scaling rectangle layers. Smaller values +// were causing visual anomalies. +const float kMinimumRectScale = 0.0001f; + +// The minimum scale factor to use when scaling circle layers. Smaller values +// were causing visual anomalies. +const float kMinimumCircleScale = 0.001f; + +// All the sub animations that are used to animate each of the InkDropStates. +// These are used to get time durations with +// GetAnimationDuration(InkDropSubAnimations). Note that in general a sub +// animation defines the duration for either a transformation animation or an +// opacity animation but there are some exceptions where an entire InkDropState +// animation consists of only 1 sub animation and it defines the duration for +// both the transformation and opacity animations. +enum InkDropSubAnimations { + // HIDDEN sub animations. + + // The HIDDEN sub animation that is fading out to a hidden opacity. + HIDDEN_FADE_OUT, + + // The HIDDEN sub animation that transforms the shape to a |small_size_| + // circle. + HIDDEN_TRANSFORM, + + // ACTION_PENDING sub animations. + + // The ACTION_PENDING sub animation that fades in to the visible opacity. + ACTION_PENDING_FADE_IN, + + // The ACTION_PENDING sub animation that transforms the shape to a + // |large_size_| circle. + ACTION_PENDING_TRANSFORM, + + // ACTION_TRIGGERED sub animations. + + // The ACTION_TRIGGERED sub animation that is fading out to a hidden opacity. + ACTION_TRIGGERED_FADE_OUT, + + // The ACTION_TRIGGERED sub animation that transforms the shape to a + // |large_size_| + // circle. + ACTION_TRIGGERED_TRANSFORM, + + // ALTERNATE_ACTION_PENDING sub animations. + + // The ALTERNATE_ACTION_PENDING animation has only one sub animation which + // animates + // to a |small_size_| rounded rectangle at visible opacity. + ALTERNATE_ACTION_PENDING, + + // ALTERNATE_ACTION_TRIGGERED sub animations. + + // The ALTERNATE_ACTION_TRIGGERED sub animation that is fading out to a hidden + // opacity. + ALTERNATE_ACTION_TRIGGERED_FADE_OUT, + + // The ALTERNATE_ACTION_TRIGGERED sub animation that transforms the shape to a + // |large_size_| + // rounded rectangle. + ALTERNATE_ACTION_TRIGGERED_TRANSFORM, + + // ACTIVATED sub animations. + + // The ACTIVATED sub animation that transforms the shape to a |large_size_| + // circle. This is used when the ink drop is in a HIDDEN state prior to + // animating to the ACTIVATED state. + ACTIVATED_CIRCLE_TRANSFORM, + + // The ACTIVATED sub animation that transforms the shape to a |small_size_| + // rounded rectangle. + ACTIVATED_RECT_TRANSFORM, + + // DEACTIVATED sub animations. + + // The DEACTIVATED sub animation that is fading out to a hidden opacity. + DEACTIVATED_FADE_OUT, + + // The DEACTIVATED sub animation that transforms the shape to a |large_size_| + // rounded rectangle. + DEACTIVATED_TRANSFORM, +}; + +// The scale factor used to burst the ACTION_TRIGGERED bubble as it fades out. +const float kQuickActionBurstScale = 1.3f; + +// Duration constants for InkDropStateSubAnimations. See the +// InkDropStateSubAnimations enum documentation for more info. +int kAnimationDurationInMs[] = { + 150, // HIDDEN_FADE_OUT + 200, // HIDDEN_TRANSFORM + 0, // ACTION_PENDING_FADE_IN + 160, // ACTION_PENDING_TRANSFORM + 150, // ACTION_TRIGGERED_FADE_OUT + 160, // ACTION_TRIGGERED_TRANSFORM + 200, // ALTERNATE_ACTION_PENDING + 150, // ALTERNATE_ACTION_TRIGGERED_FADE_OUT + 200, // ALTERNATE_ACTION_TRIGGERED_TRANSFORM + 200, // ACTIVATED_CIRCLE_TRANSFORM + 160, // ACTIVATED_RECT_TRANSFORM + 150, // DEACTIVATED_FADE_OUT + 200, // DEACTIVATED_TRANSFORM +}; + +// Returns the InkDropState sub animation duration for the given |state|. +base::TimeDelta GetAnimationDuration(InkDropSubAnimations state) { + return base::TimeDelta::FromMilliseconds( + (views::InkDropAnimation::UseFastAnimations() + ? 1 + : views::InkDropAnimation::kSlowAnimationDurationFactor) * + kAnimationDurationInMs[state]); +} + +// Calculates a Transform for a circle layer. The transform will be set up to +// translate the |drawn_center_point| to the origin, scale, and then translate +// to the target point defined by |target_center_x| and |target_center_y|. +gfx::Transform CalculateCircleTransform(const gfx::Point& drawn_center_point, + float scale, + float target_center_x, + float target_center_y) { + gfx::Transform transform; + transform.Translate(target_center_x, target_center_y); + transform.Scale(scale, scale); + transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); + return transform; +} + +// Calculates a Transform for a rectangle layer. The transform will be set up to +// translate the |drawn_center_point| to the origin and then scale by the +// |x_scale| and |y_scale| factors. +gfx::Transform CalculateRectTransform(const gfx::Point& drawn_center_point, + float x_scale, + float y_scale) { + gfx::Transform transform; + transform.Scale(x_scale, y_scale); + transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); + return transform; +} + +} // namespace + +namespace views { + +SquareInkDropAnimation::SquareInkDropAnimation(const gfx::Size& large_size, + int large_corner_radius, + const gfx::Size& small_size, + int small_corner_radius, + const gfx::Point& center_point, + SkColor color) + : large_size_(large_size), + large_corner_radius_(large_corner_radius), + small_size_(small_size), + small_corner_radius_(small_corner_radius), + circle_layer_delegate_(new CircleLayerDelegate( + color, + std::min(large_size_.width(), large_size_.height()) / 2)), + rect_layer_delegate_(new RectangleLayerDelegate(color, large_size_)), + root_layer_(ui::LAYER_NOT_DRAWN) { + root_layer_.set_name("SquareInkDropAnimation:ROOT_LAYER"); + + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + AddPaintLayer(static_cast<PaintedShape>(i)); + + root_layer_.SetMasksToBounds(false); + root_layer_.SetBounds(gfx::Rect(large_size_)); + + gfx::Transform transform; + transform.Translate(center_point.x(), center_point.y()); + root_layer_.SetTransform(transform); + + SetStateToHidden(); +} + +SquareInkDropAnimation::~SquareInkDropAnimation() { + // Explicitly aborting all the animations ensures all callbacks are invoked + // while this instance still exists. + AbortAllAnimations(); +} + +void SquareInkDropAnimation::SnapToActivated() { + InkDropAnimation::SnapToActivated(); + SetOpacity(kVisibleOpacity); + InkDropTransforms transforms; + GetActivatedTargetTransforms(&transforms); + SetTransforms(transforms); +} + +ui::Layer* SquareInkDropAnimation::GetRootLayer() { + return &root_layer_; +} + +bool SquareInkDropAnimation::IsVisible() const { + return root_layer_.visible(); +} + +float SquareInkDropAnimation::GetCurrentOpacity() const { + return root_layer_.opacity(); +} + +std::string SquareInkDropAnimation::ToLayerName(PaintedShape painted_shape) { + switch (painted_shape) { + case TOP_LEFT_CIRCLE: + return "TOP_LEFT_CIRCLE"; + case TOP_RIGHT_CIRCLE: + return "TOP_RIGHT_CIRCLE"; + case BOTTOM_RIGHT_CIRCLE: + return "BOTTOM_RIGHT_CIRCLE"; + case BOTTOM_LEFT_CIRCLE: + return "BOTTOM_LEFT_CIRCLE"; + case HORIZONTAL_RECT: + return "HORIZONTAL_RECT"; + case VERTICAL_RECT: + return "VERTICAL_RECT"; + case PAINTED_SHAPE_COUNT: + NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used."; + return "PAINTED_SHAPE_COUNT"; + } + return "UNKNOWN"; +} + +void SquareInkDropAnimation::AnimateStateChange( + InkDropState old_ink_drop_state, + InkDropState new_ink_drop_state, + ui::LayerAnimationObserver* animation_observer) { + InkDropTransforms transforms; + + switch (new_ink_drop_state) { + case InkDropState::HIDDEN: + if (!IsVisible()) { + SetStateToHidden(); + break; + } else { + AnimateToOpacity(kHiddenOpacity, GetAnimationDuration(HIDDEN_FADE_OUT), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateCircleTransforms(small_size_, &transforms); + AnimateToTransforms( + transforms, GetAnimationDuration(HIDDEN_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + } + break; + case InkDropState::ACTION_PENDING: + DCHECK(old_ink_drop_state == InkDropState::HIDDEN); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ACTION_PENDING_FADE_IN), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ACTION_PENDING_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + CalculateCircleTransforms(large_size_, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(ACTION_PENDING_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + 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); + if (old_ink_drop_state == InkDropState::HIDDEN) { + AnimateStateChange(old_ink_drop_state, InkDropState::ACTION_PENDING, + animation_observer); + } + AnimateToOpacity(kHiddenOpacity, + GetAnimationDuration(ACTION_TRIGGERED_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + gfx::Size s = ScaleToRoundedSize(large_size_, kQuickActionBurstScale); + CalculateCircleTransforms(s, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(ACTION_TRIGGERED_TRANSFORM), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::ALTERNATE_ACTION_PENDING: + DCHECK(old_ink_drop_state == InkDropState::ACTION_PENDING); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ALTERNATE_ACTION_PENDING), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + CalculateRectTransforms(small_size_, small_corner_radius_, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(ALTERNATE_ACTION_PENDING), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + case InkDropState::ALTERNATE_ACTION_TRIGGERED: { + DCHECK(old_ink_drop_state == InkDropState::ALTERNATE_ACTION_PENDING); + base::TimeDelta visible_duration = + GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_TRANSFORM) - + GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_FADE_OUT); + AnimateToOpacity(kVisibleOpacity, visible_duration, + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + AnimateToOpacity(kHiddenOpacity, GetAnimationDuration( + ALTERNATE_ACTION_TRIGGERED_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); + AnimateToTransforms(transforms, GetAnimationDuration( + ALTERNATE_ACTION_TRIGGERED_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::ACTIVATED: { + // Animate the opacity so that it cancels any opacity animations already + // in progress. + AnimateToOpacity(kVisibleOpacity, base::TimeDelta(), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + + ui::LayerAnimator::PreemptionStrategy rect_transform_preemption_strategy = + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET; + if (old_ink_drop_state == InkDropState::HIDDEN) { + rect_transform_preemption_strategy = + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION; + CalculateCircleTransforms(large_size_, &transforms); + AnimateToTransforms( + transforms, GetAnimationDuration(ACTIVATED_CIRCLE_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + } else if (old_ink_drop_state == InkDropState::ACTION_PENDING) { + rect_transform_preemption_strategy = + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION; + } + + GetActivatedTargetTransforms(&transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(ACTIVATED_RECT_TRANSFORM), + rect_transform_preemption_strategy, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::DEACTIVATED: { + base::TimeDelta visible_duration = + GetAnimationDuration(DEACTIVATED_TRANSFORM) - + GetAnimationDuration(DEACTIVATED_FADE_OUT); + AnimateToOpacity(kVisibleOpacity, visible_duration, + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); + AnimateToOpacity(kHiddenOpacity, + GetAnimationDuration(DEACTIVATED_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(DEACTIVATED_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + } +} + +void SquareInkDropAnimation::SetStateToHidden() { + InkDropTransforms transforms; + // Use non-zero size to avoid visual anomalies. + CalculateCircleTransforms(gfx::Size(1, 1), &transforms); + SetTransforms(transforms); + root_layer_.SetOpacity(InkDropAnimation::kHiddenOpacity); + root_layer_.SetVisible(false); +} + +void SquareInkDropAnimation::AbortAllAnimations() { + root_layer_.GetAnimator()->AbortAllAnimations(); + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + painted_layers_[i]->GetAnimator()->AbortAllAnimations(); +} + +void SquareInkDropAnimation::AnimateToTransforms( + const InkDropTransforms transforms, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* animation_observer) { + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) { + ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator(); + ui::ScopedLayerAnimationSettings animation(animator); + animation.SetPreemptionStrategy(preemption_strategy); + animation.SetTweenType(tween); + ui::LayerAnimationElement* element = + ui::LayerAnimationElement::CreateTransformElement(transforms[i], + duration); + ui::LayerAnimationSequence* sequence = + new ui::LayerAnimationSequence(element); + + if (animation_observer) + sequence->AddObserver(animation_observer); + + animator->StartAnimation(sequence); + } +} + +void SquareInkDropAnimation::SetTransforms(const InkDropTransforms transforms) { + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + painted_layers_[i]->SetTransform(transforms[i]); +} + +void SquareInkDropAnimation::SetOpacity(float opacity) { + root_layer_.SetOpacity(opacity); +} + +void SquareInkDropAnimation::AnimateToOpacity( + float opacity, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* animation_observer) { + ui::LayerAnimator* animator = root_layer_.GetAnimator(); + ui::ScopedLayerAnimationSettings animation_settings(animator); + animation_settings.SetPreemptionStrategy(preemption_strategy); + animation_settings.SetTweenType(tween); + ui::LayerAnimationElement* animation_element = + ui::LayerAnimationElement::CreateOpacityElement(opacity, duration); + ui::LayerAnimationSequence* animation_sequence = + new ui::LayerAnimationSequence(animation_element); + + if (animation_observer) + animation_sequence->AddObserver(animation_observer); + + animator->StartAnimation(animation_sequence); +} + +void SquareInkDropAnimation::CalculateCircleTransforms( + const gfx::Size& size, + InkDropTransforms* transforms_out) const { + CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f, + transforms_out); +} + +void SquareInkDropAnimation::CalculateRectTransforms( + const gfx::Size& size, + float corner_radius, + InkDropTransforms* transforms_out) const { + DCHECK_GE(size.width() / 2.0f, corner_radius) + << "The circle's diameter should not be greater than the total width."; + DCHECK_GE(size.height() / 2.0f, corner_radius) + << "The circle's diameter should not be greater than the total height."; + + // The shapes are drawn such that their center points are not at the origin. + // Thus we use the CalculateCircleTransform() and CalculateRectTransform() + // methods to calculate the complex Transforms. + + const float circle_scale = std::max( + kMinimumCircleScale, + corner_radius / static_cast<float>(circle_layer_delegate_->radius())); + + const float circle_target_x_offset = size.width() / 2.0f - corner_radius; + const float circle_target_y_offset = size.height() / 2.0f - corner_radius; + + (*transforms_out)[TOP_LEFT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + -circle_target_x_offset, -circle_target_y_offset); + + (*transforms_out)[TOP_RIGHT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + circle_target_x_offset, -circle_target_y_offset); + + (*transforms_out)[BOTTOM_RIGHT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + circle_target_x_offset, circle_target_y_offset); + + (*transforms_out)[BOTTOM_LEFT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + -circle_target_x_offset, circle_target_y_offset); + + const float rect_delegate_width = + static_cast<float>(rect_layer_delegate_->size().width()); + const float rect_delegate_height = + static_cast<float>(rect_layer_delegate_->size().height()); + + (*transforms_out)[HORIZONTAL_RECT] = CalculateRectTransform( + ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), + std::max(kMinimumRectScale, size.width() / rect_delegate_width), + std::max(kMinimumRectScale, + (size.height() - 2.0f * corner_radius) / rect_delegate_height)); + + (*transforms_out)[VERTICAL_RECT] = CalculateRectTransform( + ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), + std::max(kMinimumRectScale, + (size.width() - 2.0f * corner_radius) / rect_delegate_width), + std::max(kMinimumRectScale, size.height() / rect_delegate_height)); +} + +void SquareInkDropAnimation::GetCurrentTransforms( + InkDropTransforms* transforms_out) const { + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + (*transforms_out)[i] = painted_layers_[i]->transform(); +} + +void SquareInkDropAnimation::GetActivatedTargetTransforms( + InkDropTransforms* transforms_out) const { + CalculateRectTransforms(small_size_, small_corner_radius_, transforms_out); +} + +void SquareInkDropAnimation::AddPaintLayer(PaintedShape painted_shape) { + ui::LayerDelegate* delegate = nullptr; + switch (painted_shape) { + case TOP_LEFT_CIRCLE: + case TOP_RIGHT_CIRCLE: + case BOTTOM_RIGHT_CIRCLE: + case BOTTOM_LEFT_CIRCLE: + delegate = circle_layer_delegate_.get(); + break; + case HORIZONTAL_RECT: + case VERTICAL_RECT: + delegate = rect_layer_delegate_.get(); + break; + case PAINTED_SHAPE_COUNT: + NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type."; + break; + } + + ui::Layer* layer = new ui::Layer(); + root_layer_.Add(layer); + + layer->SetBounds(gfx::Rect(large_size_)); + layer->SetFillsBoundsOpaquely(false); + layer->set_delegate(delegate); + layer->SetVisible(true); + layer->SetOpacity(1.0); + layer->SetMasksToBounds(false); + layer->set_name("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape)); + + painted_layers_[painted_shape].reset(layer); +} + +} // namespace views diff --git a/chromium/ui/views/animation/square_ink_drop_animation.h b/chromium/ui/views/animation/square_ink_drop_animation.h new file mode 100644 index 00000000000..d704e7eb4af --- /dev/null +++ b/chromium/ui/views/animation/square_ink_drop_animation.h @@ -0,0 +1,184 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_SQUARE_INK_DROP_ANIMATION_H_ +#define UI_VIEWS_ANIMATION_SQUARE_INK_DROP_ANIMATION_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animator.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/transform.h" +#include "ui/views/animation/ink_drop_animation.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/views_export.h" + +namespace ui { +class Layer; +} // namespace ui + +namespace views { +class CircleLayerDelegate; +class RectangleLayerDelegate; + +namespace test { +class SquareInkDropAnimationTestApi; +} // namespace test + +// An ink drop animation that smoothly animates between a circle and a rounded +// rectangle of different sizes for each of the different InkDropStates. The +// final frame for each InkDropState will be bounded by either a |large_size_| +// rectangle or a |small_size_| rectangle. +// +// The valid InkDropState transitions are defined below: +// +// {All InkDropStates} => HIDDEN +// HIDDEN => ACTION_PENDING +// HIDDEN, ACTION_PENDING => ACTION_TRIGGERED +// ACTION_PENDING => ALTERNATE_ACTION_PENDING +// ALTERNATE_ACTION_PENDING => ALTERNATE_ACTION_TRIGGERED +// {All InkDropStates} => ACTIVATED +// {All InkDropStates} => DEACTIVATED +// +class VIEWS_EXPORT SquareInkDropAnimation : public InkDropAnimation { + public: + SquareInkDropAnimation(const gfx::Size& large_size, + int large_corner_radius, + const gfx::Size& small_size, + int small_corner_radius, + const gfx::Point& center_point, + SkColor color); + ~SquareInkDropAnimation() override; + + // InkDropAnimation: + void SnapToActivated() override; + ui::Layer* GetRootLayer() override; + bool IsVisible() const override; + + private: + friend class test::SquareInkDropAnimationTestApi; + + // Enumeration of the different shapes that compose the ink drop. + enum PaintedShape { + TOP_LEFT_CIRCLE = 0, + TOP_RIGHT_CIRCLE, + BOTTOM_RIGHT_CIRCLE, + BOTTOM_LEFT_CIRCLE, + HORIZONTAL_RECT, + VERTICAL_RECT, + // The total number of shapes, not an actual shape. + PAINTED_SHAPE_COUNT + }; + + // Returns a human readable string for the |painted_shape| value. + static std::string ToLayerName(PaintedShape painted_shape); + + // Type that contains a gfx::Tansform for each of the layers required by the + // ink drop. + typedef gfx::Transform InkDropTransforms[PAINTED_SHAPE_COUNT]; + + float GetCurrentOpacity() const; + + // InkDropAnimation: + void AnimateStateChange(InkDropState old_ink_drop_state, + InkDropState new_ink_drop_state, + ui::LayerAnimationObserver* observer) override; + void SetStateToHidden() override; + void AbortAllAnimations() override; + + // Animates all of the painted shape layers to the specified |transforms|. The + // animation will be configured with the given |duration|, |tween|, and + // |preemption_strategy| values. The |observer| will be added to all + // LayerAnimationSequences if not null. + void AnimateToTransforms( + const InkDropTransforms transforms, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* observer); + + // Sets the |transforms| on all of the shape layers. Note that this does not + // perform any animation. + void SetTransforms(const InkDropTransforms transforms); + + // Sets the opacity of the ink drop. Note that this does not perform any + // animation. + void SetOpacity(float opacity); + + // Animates all of the painted shape layers to the specified |opacity|. The + // animation will be configured with the given |duration|, |tween|, and + // |preemption_strategy| values. The |observer| will be added to all + // LayerAnimationSequences if not null. + void AnimateToOpacity( + float opacity, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* observer); + + // Updates all of the Transforms in |transforms_out| for a circle of the given + // |size|. + void CalculateCircleTransforms(const gfx::Size& size, + InkDropTransforms* transforms_out) const; + + // Updates all of the Transforms in |transforms_out| for a rounded rectangle + // of the given |size| and |corner_radius|. + void CalculateRectTransforms(const gfx::Size& size, + float corner_radius, + InkDropTransforms* transforms_out) const; + + // Updates all of the Transforms in |transforms_out| to the current Transforms + // of the painted shape Layers. + void GetCurrentTransforms(InkDropTransforms* transforms_out) const; + + // Updates all of the Transforms in |transforms_out| with the target + // Transforms for the ACTIVATED animation. + void GetActivatedTargetTransforms(InkDropTransforms* transforms_out) const; + + // Adds and configures a new |painted_shape| layer to |painted_layers_|. + void AddPaintLayer(PaintedShape painted_shape); + + // Maximum size that an ink drop will be drawn to for any InkDropState whose + // final frame should be large. + gfx::Size large_size_; + + // Corner radius used to draw the rounded rectangles corner for any + // InkDropState whose final frame should be large. + int large_corner_radius_; + + // Maximum size that an ink drop will be drawn to for any InkDropState whose + // final frame should be small. + gfx::Size small_size_; + + // Corner radius used to draw the rounded rectangles corner for any + // InkDropState whose final frame should be small. + int small_corner_radius_; + + // ui::LayerDelegate to paint circles for all the circle layers. + scoped_ptr<CircleLayerDelegate> circle_layer_delegate_; + + // ui::LayerDelegate to paint rectangles for all the rectangle layers. + scoped_ptr<RectangleLayerDelegate> rect_layer_delegate_; + + // The root layer that parents the animating layers. The root layer is used to + // manipulate opacity and location, and its children are used to manipulate + // the different painted shapes that compose the ink drop. + ui::Layer root_layer_; + + // ui::Layers for all of the painted shape layers that compose the ink drop. + scoped_ptr<ui::Layer> painted_layers_[PAINTED_SHAPE_COUNT]; + + DISALLOW_COPY_AND_ASSIGN(SquareInkDropAnimation); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_SQUARE_INK_DROP_ANIMATION_H_ diff --git a/chromium/ui/views/animation/square_ink_drop_animation_unittest.cc b/chromium/ui/views/animation/square_ink_drop_animation_unittest.cc new file mode 100644 index 00000000000..0b11630ca5e --- /dev/null +++ b/chromium/ui/views/animation/square_ink_drop_animation_unittest.cc @@ -0,0 +1,248 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_UNITTEST_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_UNITTEST_H_ + +#include "ui/views/animation/square_ink_drop_animation.h" + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/views/animation/ink_drop_animation_observer.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/animation/test/square_ink_drop_animation_test_api.h" +#include "ui/views/animation/test/test_ink_drop_animation_observer.h" + +namespace views { +namespace test { + +namespace { + +using PaintedShape = views::test::SquareInkDropAnimationTestApi::PaintedShape; + +// Transforms a copy of |point| with |transform| and returns it. +gfx::Point TransformPoint(const gfx::Transform& transform, + const gfx::Point& point) { + gfx::Point transformed_point = point; + transform.TransformPoint(&transformed_point); + return transformed_point; +} + +class SquareInkDropAnimationCalculateTransformsTest : public testing::Test { + public: + SquareInkDropAnimationCalculateTransformsTest(); + ~SquareInkDropAnimationCalculateTransformsTest() override; + + protected: + // Half the width/height of the drawn ink drop. + static const int kHalfDrawnSize; + + // The full width/height of the drawn ink drop. + static const int kDrawnSize; + + // The radius of the rounded rectangle corners. + static const int kTransformedRadius; + + // Half the width/height of the transformed ink drop. + static const int kHalfTransformedSize; + + // The full width/height of the transformed ink drop. + static const int kTransformedSize; + + // Constant points in the drawn space that will be transformed. + static const gfx::Point kDrawnCenterPoint; + static const gfx::Point kDrawnMidLeftPoint; + static const gfx::Point kDrawnMidRightPoint; + static const gfx::Point kDrawnTopMidPoint; + static const gfx::Point kDrawnBottomMidPoint; + + // The test target. + SquareInkDropAnimation ink_drop_animation_; + + // Provides internal access to the test target. + SquareInkDropAnimationTestApi test_api_; + + // The gfx::Transforms collection that is populated via the + // Calculate*Transforms() calls. + SquareInkDropAnimationTestApi::InkDropTransforms transforms_; + + private: + DISALLOW_COPY_AND_ASSIGN(SquareInkDropAnimationCalculateTransformsTest); +}; + +const int SquareInkDropAnimationCalculateTransformsTest::kHalfDrawnSize = 5; +const int SquareInkDropAnimationCalculateTransformsTest::kDrawnSize = + 2 * kHalfDrawnSize; + +const int SquareInkDropAnimationCalculateTransformsTest::kTransformedRadius = + 10; +const int SquareInkDropAnimationCalculateTransformsTest::kHalfTransformedSize = + 100; +const int SquareInkDropAnimationCalculateTransformsTest::kTransformedSize = + 2 * kHalfTransformedSize; + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnCenterPoint = + gfx::Point(kHalfDrawnSize, kHalfDrawnSize); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnMidLeftPoint = + gfx::Point(0, kHalfDrawnSize); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnMidRightPoint = + gfx::Point(kDrawnSize, kHalfDrawnSize); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnTopMidPoint = + gfx::Point(kHalfDrawnSize, 0); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnBottomMidPoint = + gfx::Point(kHalfDrawnSize, kDrawnSize); + +SquareInkDropAnimationCalculateTransformsTest:: + SquareInkDropAnimationCalculateTransformsTest() + : ink_drop_animation_(gfx::Size(kDrawnSize, kDrawnSize), + 2, + gfx::Size(kHalfDrawnSize, kHalfDrawnSize), + 1, + gfx::Point(), + SK_ColorBLACK), + test_api_(&ink_drop_animation_) {} + +SquareInkDropAnimationCalculateTransformsTest:: + ~SquareInkDropAnimationCalculateTransformsTest() {} + +} // namespace + +TEST_F(SquareInkDropAnimationCalculateTransformsTest, + TransformedPointsUsingTransformsFromCalculateCircleTransforms) { + test_api_.CalculateCircleTransforms( + gfx::Size(kTransformedSize, kTransformedSize), &transforms_); + + struct { + PaintedShape shape; + gfx::Point center_point; + gfx::Point mid_left_point; + gfx::Point mid_right_point; + gfx::Point top_mid_point; + gfx::Point bottom_mid_point; + } test_cases[] = { + {PaintedShape::TOP_LEFT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::TOP_RIGHT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::BOTTOM_RIGHT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::BOTTOM_LEFT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::HORIZONTAL_RECT, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), gfx::Point(0, 0), gfx::Point(0, 0)}, + {PaintedShape::VERTICAL_RECT, gfx::Point(0, 0), gfx::Point(0, 0), + gfx::Point(0, 0), gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}}; + + for (size_t i = 0; i < arraysize(test_cases); ++i) { + PaintedShape shape = test_cases[i].shape; + SCOPED_TRACE(testing::Message() << "i=" << i << " shape=" << shape); + gfx::Transform transform = transforms_[shape]; + + EXPECT_EQ(test_cases[i].center_point, + TransformPoint(transform, kDrawnCenterPoint)); + EXPECT_EQ(test_cases[i].mid_left_point, + TransformPoint(transform, kDrawnMidLeftPoint)); + EXPECT_EQ(test_cases[i].mid_right_point, + TransformPoint(transform, kDrawnMidRightPoint)); + EXPECT_EQ(test_cases[i].top_mid_point, + TransformPoint(transform, kDrawnTopMidPoint)); + EXPECT_EQ(test_cases[i].bottom_mid_point, + TransformPoint(transform, kDrawnBottomMidPoint)); + } +} + +TEST_F(SquareInkDropAnimationCalculateTransformsTest, + TransformedPointsUsingTransformsFromCalculateRectTransforms) { + test_api_.CalculateRectTransforms( + gfx::Size(kTransformedSize, kTransformedSize), kTransformedRadius, + &transforms_); + + const int x_offset = kHalfTransformedSize - kTransformedRadius; + const int y_offset = kHalfTransformedSize - kTransformedRadius; + + struct { + PaintedShape shape; + gfx::Point center_point; + gfx::Point mid_left_point; + gfx::Point mid_right_point; + gfx::Point top_mid_point; + gfx::Point bottom_mid_point; + } test_cases[] = { + {PaintedShape::TOP_LEFT_CIRCLE, gfx::Point(-x_offset, -y_offset), + gfx::Point(-kHalfTransformedSize, -y_offset), + gfx::Point(-x_offset + kTransformedRadius, -y_offset), + gfx::Point(-x_offset, -kHalfTransformedSize), + gfx::Point(-x_offset, -y_offset + kTransformedRadius)}, + {PaintedShape::TOP_RIGHT_CIRCLE, gfx::Point(x_offset, -y_offset), + gfx::Point(x_offset - kTransformedRadius, -y_offset), + gfx::Point(kHalfTransformedSize, -y_offset), + gfx::Point(x_offset, -kHalfTransformedSize), + gfx::Point(x_offset, -y_offset + kTransformedRadius)}, + {PaintedShape::BOTTOM_RIGHT_CIRCLE, gfx::Point(x_offset, y_offset), + gfx::Point(x_offset - kTransformedRadius, y_offset), + gfx::Point(kHalfTransformedSize, y_offset), + gfx::Point(x_offset, y_offset - kTransformedRadius), + gfx::Point(x_offset, kHalfTransformedSize)}, + {PaintedShape::BOTTOM_LEFT_CIRCLE, gfx::Point(-x_offset, y_offset), + gfx::Point(-kHalfTransformedSize, y_offset), + gfx::Point(-x_offset + kTransformedRadius, y_offset), + gfx::Point(-x_offset, y_offset - kTransformedRadius), + gfx::Point(-x_offset, kHalfTransformedSize)}, + {PaintedShape::HORIZONTAL_RECT, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), gfx::Point(0, -y_offset), + gfx::Point(0, y_offset)}, + {PaintedShape::VERTICAL_RECT, gfx::Point(0, 0), gfx::Point(-x_offset, 0), + gfx::Point(x_offset, 0), gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}}; + + for (size_t i = 0; i < arraysize(test_cases); ++i) { + PaintedShape shape = test_cases[i].shape; + SCOPED_TRACE(testing::Message() << "i=" << i << " shape=" << shape); + gfx::Transform transform = transforms_[shape]; + + EXPECT_EQ(test_cases[i].center_point, + TransformPoint(transform, kDrawnCenterPoint)); + EXPECT_EQ(test_cases[i].mid_left_point, + TransformPoint(transform, kDrawnMidLeftPoint)); + EXPECT_EQ(test_cases[i].mid_right_point, + TransformPoint(transform, kDrawnMidRightPoint)); + EXPECT_EQ(test_cases[i].top_mid_point, + TransformPoint(transform, kDrawnTopMidPoint)); + EXPECT_EQ(test_cases[i].bottom_mid_point, + TransformPoint(transform, kDrawnBottomMidPoint)); + } +} + +} // namespace test +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_UNITTEST_H_ diff --git a/chromium/ui/views/background.cc b/chromium/ui/views/background.cc index 5338f37b2f1..56f1eb36405 100644 --- a/chromium/ui/views/background.cc +++ b/chromium/ui/views/background.cc @@ -61,34 +61,16 @@ class BackgroundPainter : public Background { Background::Background() : color_(SK_ColorWHITE) -#if defined(OS_WIN) - , native_control_brush_(NULL) -#endif { } Background::~Background() { -#if defined(OS_WIN) - DeleteObject(native_control_brush_); -#endif } void Background::SetNativeControlColor(SkColor color) { color_ = color; -#if defined(OS_WIN) - DeleteObject(native_control_brush_); - native_control_brush_ = NULL; -#endif } -#if defined(OS_WIN) -HBRUSH Background::GetNativeControlBrush() const { - if (!native_control_brush_) - native_control_brush_ = CreateSolidBrush(skia::SkColorToCOLORREF(color_)); - return native_control_brush_; -} -#endif - // static Background* Background::CreateSolidBackground(SkColor color) { return new SolidBackground(color); diff --git a/chromium/ui/views/background.h b/chromium/ui/views/background.h index 0d7d65cdcd6..d00418ad453 100644 --- a/chromium/ui/views/background.h +++ b/chromium/ui/views/background.h @@ -90,19 +90,8 @@ class VIEWS_EXPORT Background { // backgrounds, this is not useful (returns a default color). SkColor get_color() const { return color_; } -#if defined(OS_WIN) - // TODO(port): Make GetNativeControlBrush portable (currently uses HBRUSH). - - // Get the brush that was specified by SetNativeControlColor - HBRUSH GetNativeControlBrush() const; -#endif // defined(OS_WIN) - private: SkColor color_; -#if defined(OS_WIN) - // TODO(port): Create portable replacement for HBRUSH. - mutable HBRUSH native_control_brush_; -#endif // defined(OS_WIN) DISALLOW_COPY_AND_ASSIGN(Background); }; diff --git a/chromium/ui/views/border.cc b/chromium/ui/views/border.cc index 9b87180e1eb..a7a11440923 100644 --- a/chromium/ui/views/border.cc +++ b/chromium/ui/views/border.cc @@ -99,7 +99,7 @@ void RoundedRectBorder::Paint(const View& view, gfx::Canvas* canvas) { } gfx::Insets RoundedRectBorder::GetInsets() const { - return gfx::Insets(thickness_, thickness_, thickness_, thickness_); + return gfx::Insets(thickness_); } gfx::Size RoundedRectBorder::GetMinimumSize() const { @@ -184,8 +184,7 @@ scoped_ptr<Border> Border::NullBorder() { // static scoped_ptr<Border> Border::CreateSolidBorder(int thickness, SkColor color) { - return make_scoped_ptr(new SolidSidedBorder( - gfx::Insets(thickness, thickness, thickness, thickness), color)); + return make_scoped_ptr(new SolidSidedBorder(gfx::Insets(thickness), color)); } // static diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc index c2892ca0fc7..f81bb2d7618 100644 --- a/chromium/ui/views/bubble/bubble_border.cc +++ b/chromium/ui/views/bubble/bubble_border.cc @@ -12,6 +12,7 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/path.h" #include "ui/gfx/skia_util.h" #include "ui/resources/grit/ui_resources.h" #include "ui/views/painter.h" @@ -144,8 +145,13 @@ BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) shadow_(shadow), background_color_(color), use_theme_background_color_(false) { - DCHECK(shadow < SHADOW_COUNT); - images_ = GetBorderImages(shadow); +#if defined(OS_MACOSX) + // On Mac, use the NO_ASSETS bubble border. WindowServer on Mac is able to + // generate drop shadows for dialogs, hence we don't use raster shadows. + shadow_ = NO_ASSETS; +#endif // OS_MACOSX + DCHECK(shadow_ < SHADOW_COUNT); + images_ = GetBorderImages(shadow_); } BubbleBorder::~BubbleBorder() {} @@ -158,8 +164,14 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, int h = anchor_rect.height(); const gfx::Size size(GetSizeForContentsSize(contents_size)); const int arrow_offset = GetArrowOffset(size); - const int arrow_size = + // |arrow_shift| is necessary to visually align the tip of the bubble arrow + // with the anchor point. This shift is an inverse of the shadow thickness. + int arrow_shift = images_->arrow_interior_thickness + kStroke - images_->arrow_thickness; + // When arrow is painted transparently the visible border of the bubble needs + // to be positioned at the same bounds as when the arrow is shown. + if (arrow_paint_type_ == PAINT_TRANSPARENT) + arrow_shift += images_->arrow_interior_thickness; const bool mid_anchor = alignment_ == ALIGN_ARROW_TO_MID_ANCHOR; // Calculate the bubble coordinates based on the border and arrow settings. @@ -172,9 +184,11 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, x += mid_anchor ? w / 2 + arrow_offset - size.width() : w - size.width() + GetBorderThickness() - kStroke; } - y += is_arrow_on_top(arrow_) ? h + arrow_size : -arrow_size - size.height(); + y += is_arrow_on_top(arrow_) ? h + arrow_shift + : -arrow_shift - size.height(); } else if (has_arrow(arrow_)) { - x += is_arrow_on_left(arrow_) ? w + arrow_size : -arrow_size - size.width(); + x += is_arrow_on_left(arrow_) ? w + arrow_shift + : -arrow_shift - size.width(); if (is_arrow_on_top(arrow_)) { y += mid_anchor ? h / 2 - arrow_offset : kStroke - GetBorderThickness(); } else if (is_arrow_at_center(arrow_)) { @@ -211,6 +225,15 @@ int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const { return std::max(min, std::min(arrow_offset_, edge_length - min)); } +bool BubbleBorder::GetArrowPath(const gfx::Rect& view_bounds, + gfx::Path* path) const { + if (!has_arrow(arrow_) || arrow_paint_type_ != PAINT_NORMAL) + return false; + + GetArrowPathFromArrowBounds(GetArrowRect(view_bounds), path); + return true; +} + void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { gfx::Rect bounds(view.GetContentsBounds()); bounds.Inset(-GetBorderThickness(), -GetBorderThickness()); @@ -238,8 +261,8 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { gfx::Insets BubbleBorder::GetInsets() const { // The insets contain the stroke and shadow pixels outside the bubble fill. const int inset = GetBorderThickness(); - if ((arrow_paint_type_ == PAINT_NONE) || !has_arrow(arrow_)) - return gfx::Insets(inset, inset, inset, inset); + if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) + return gfx::Insets(inset); int first_inset = inset; int second_inset = std::max(inset, images_->arrow_thickness); @@ -264,14 +287,16 @@ gfx::Size BubbleBorder::GetSizeForContentsSize( // Ensure the bubble is large enough to not overlap border and arrow images. const int min = 2 * images_->border_thickness; + // Only take arrow image sizes into account when the bubble tip is shown. + if (arrow_paint_type_ != PAINT_NORMAL || !has_arrow(arrow_)) { + size.SetToMax(gfx::Size(min, min)); + return size; + } const int min_with_arrow_width = min + images_->arrow_width; const int min_with_arrow_thickness = images_->border_thickness + std::max(images_->arrow_thickness + images_->border_interior_thickness, images_->border_thickness); - // Only take arrow image sizes into account when the bubble tip is shown. - if (arrow_paint_type_ == PAINT_NONE || !has_arrow(arrow_)) - size.SetToMax(gfx::Size(min, min)); - else if (is_arrow_on_horizontal(arrow_)) + if (is_arrow_on_horizontal(arrow_)) size.SetToMax(gfx::Size(min_with_arrow_width, min_with_arrow_thickness)); else size.SetToMax(gfx::Size(min_with_arrow_thickness, min_with_arrow_width)); @@ -328,9 +353,8 @@ gfx::Rect BubbleBorder::GetArrowRect(const gfx::Rect& bounds) const { return gfx::Rect(origin, gfx::Size(width, height)); } -void BubbleBorder::DrawArrow(gfx::Canvas* canvas, - const gfx::Rect& arrow_bounds) const { - canvas->DrawImageInt(*GetArrowImage(), arrow_bounds.x(), arrow_bounds.y()); +void BubbleBorder::GetArrowPathFromArrowBounds(const gfx::Rect& arrow_bounds, + SkPath* path) const { const bool horizontal = is_arrow_on_horizontal(arrow_); const int thickness = images_->arrow_interior_thickness; float tip_x = horizontal ? arrow_bounds.CenterPoint().x() : @@ -344,16 +368,21 @@ void BubbleBorder::DrawArrow(gfx::Canvas* canvas, const int offset_to_next_vertex = positive_offset ? images_->arrow_interior_thickness : -images_->arrow_interior_thickness; - SkPath path; - path.incReserve(4); - path.moveTo(SkDoubleToScalar(tip_x), SkDoubleToScalar(tip_y)); - path.lineTo(SkDoubleToScalar(tip_x + offset_to_next_vertex), - SkDoubleToScalar(tip_y + offset_to_next_vertex)); + path->incReserve(4); + path->moveTo(SkDoubleToScalar(tip_x), SkDoubleToScalar(tip_y)); + path->lineTo(SkDoubleToScalar(tip_x + offset_to_next_vertex), + SkDoubleToScalar(tip_y + offset_to_next_vertex)); const int multiplier = horizontal ? 1 : -1; - path.lineTo(SkDoubleToScalar(tip_x - multiplier * offset_to_next_vertex), - SkDoubleToScalar(tip_y + multiplier * offset_to_next_vertex)); - path.close(); + path->lineTo(SkDoubleToScalar(tip_x - multiplier * offset_to_next_vertex), + SkDoubleToScalar(tip_y + multiplier * offset_to_next_vertex)); + path->close(); +} +void BubbleBorder::DrawArrow(gfx::Canvas* canvas, + const gfx::Rect& arrow_bounds) const { + canvas->DrawImageInt(*GetArrowImage(), arrow_bounds.x(), arrow_bounds.y()); + SkPath path; + GetArrowPathFromArrowBounds(arrow_bounds, &path); SkPaint paint; paint.setStyle(SkPaint::kFill_Style); paint.setColor(background_color_); diff --git a/chromium/ui/views/bubble/bubble_border.h b/chromium/ui/views/bubble/bubble_border.h index fac3a719a47..057459f2539 100644 --- a/chromium/ui/views/bubble/bubble_border.h +++ b/chromium/ui/views/bubble/bubble_border.h @@ -13,7 +13,10 @@ #include "ui/views/background.h" #include "ui/views/border.h" +class SkPath; + namespace gfx { +class Path; class Rect; } @@ -120,16 +123,6 @@ class VIEWS_EXPORT BubbleBorder : public Border { BubbleBorder(Arrow arrow, Shadow shadow, SkColor color); ~BubbleBorder() override; - // Returns the radius of the corner of the border. - // TODO(xiyuan): Get rid of this since it's part of BorderImages now? - static int GetCornerRadius() { - // We can't safely calculate a border radius by comparing the sizes of the - // side and corner images, because either may have been extended in various - // directions in order to do more subtle dropshadow fading or other effects. - // So we hardcode the most accurate value. - return 4; - } - static bool has_arrow(Arrow a) { return a < NONE; } static bool is_arrow_on_left(Arrow a) { @@ -204,6 +197,13 @@ class VIEWS_EXPORT BubbleBorder : public Border { // Gets the arrow offset to use. int GetArrowOffset(const gfx::Size& border_size) const; + // Retreives the arrow path given |view_bounds|. |view_bounds| should be local + // bounds of the view. + // Returns false if |path| is unchanged, which is the case when there is no + // painted arrow. + // The returned path does not account for arrow stroke and shadow. + bool GetArrowPath(const gfx::Rect& view_bounds, gfx::Path* path) const; + // Overridden from Border: void Paint(const View& view, gfx::Canvas* canvas) override; gfx::Insets GetInsets() const override; @@ -220,6 +220,8 @@ class VIEWS_EXPORT BubbleBorder : public Border { gfx::Size GetSizeForContentsSize(const gfx::Size& contents_size) const; gfx::ImageSkia* GetArrowImage() const; gfx::Rect GetArrowRect(const gfx::Rect& bounds) const; + void GetArrowPathFromArrowBounds(const gfx::Rect& arrow_bounds, + SkPath* path) const; void DrawArrow(gfx::Canvas* canvas, const gfx::Rect& arrow_bounds) const; internal::BorderImages* GetImagesForTest() const; diff --git a/chromium/ui/views/bubble/bubble_border_unittest.cc b/chromium/ui/views/bubble/bubble_border_unittest.cc index e472e1b4fbf..31323822936 100644 --- a/chromium/ui/views/bubble/bubble_border_unittest.cc +++ b/chromium/ui/views/bubble/bubble_border_unittest.cc @@ -224,7 +224,7 @@ TEST_F(BubbleBorderTest, GetSizeForContentsSizeTest) { const gfx::Size kMediumSize = gfx::Size(50, 60); const gfx::Size kSmallHorizArrow( - 2 * kImages->border_thickness + kImages->top_arrow.width(), + 2 * kImages->border_thickness + kImages->arrow_width, kImages->border_thickness + kImages->arrow_thickness + kImages->border_interior_thickness); @@ -309,7 +309,7 @@ TEST_F(BubbleBorderTest, GetSizeForContentsSizeTest) { border.GetSizeForContentsSize(cases[i].content)); border.set_paint_arrow(BubbleBorder::PAINT_TRANSPARENT); - EXPECT_EQ(cases[i].expected_with_arrow, + EXPECT_EQ(cases[i].expected_without_arrow, border.GetSizeForContentsSize(cases[i].content)); border.set_paint_arrow(BubbleBorder::PAINT_NONE); @@ -345,19 +345,24 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { const int kArrowOffsetForHorizCenter = kTotalSizeWithHorizArrow.width() / 2; const int kArrowOffsetForVertCenter = kTotalSizeWithVertArrow.height() / 2; const int kArrowOffsetForNotCenter = - kImages->border_thickness + (kImages->top_arrow.width() / 2); - - const int kArrowSize = - kImages->arrow_interior_thickness + BubbleBorder::kStroke - - kImages->arrow_thickness; - - const int kTopHorizArrowY = kAnchor.y() + kAnchor.height() + kArrowSize; + kImages->border_thickness + (kImages->arrow_width / 2); + + const int kArrowThickness = kImages->arrow_interior_thickness; + const int kArrowShift = + kArrowThickness + BubbleBorder::kStroke - kImages->arrow_thickness; + const int kHeightDifference = kTotalSizeWithHorizArrow.height() - + kTotalSizeWithNoArrow.height(); + const int kWidthDifference = kTotalSizeWithVertArrow.width() - + kTotalSizeWithNoArrow.width(); + EXPECT_EQ(kHeightDifference, kWidthDifference); + EXPECT_EQ(kHeightDifference, kArrowThickness); + + const int kTopHorizArrowY = kAnchor.y() + kAnchor.height() + kArrowShift; const int kBottomHorizArrowY = - kAnchor.y() - kArrowSize - kTotalSizeWithHorizArrow.height(); - - const int kLeftVertArrowX = kAnchor.x() + kAnchor.width() + kArrowSize; + kAnchor.y() - kArrowShift - kTotalSizeWithHorizArrow.height(); + const int kLeftVertArrowX = kAnchor.x() + kAnchor.width() + kArrowShift; const int kRightVertArrowX = - kAnchor.x() - kArrowSize - kTotalSizeWithVertArrow.width(); + kAnchor.x() - kArrowShift - kTotalSizeWithVertArrow.width(); struct TestCase { BubbleBorder::Arrow arrow; @@ -408,12 +413,43 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { for (size_t i = 0; i < arraysize(cases); ++i) { SCOPED_TRACE(base::StringPrintf("i=%d arrow=%d alignment=%d", static_cast<int>(i), cases[i].arrow, cases[i].alignment)); - border.set_arrow(cases[i].arrow); + const BubbleBorder::Arrow arrow = cases[i].arrow; + border.set_arrow(arrow); border.set_alignment(cases[i].alignment); + border.set_paint_arrow(BubbleBorder::PAINT_NORMAL); gfx::Point origin = border.GetBounds(kAnchor, kContentSize).origin(); - EXPECT_EQ(cases[i].expected_x, origin.x()); - EXPECT_EQ(cases[i].expected_y, origin.y()); + int expected_x = cases[i].expected_x; + int expected_y = cases[i].expected_y; + EXPECT_EQ(expected_x, origin.x()); + EXPECT_EQ(expected_y, origin.y()); + + border.set_paint_arrow(BubbleBorder::PAINT_TRANSPARENT); + origin = border.GetBounds(kAnchor, kContentSize).origin(); + if (border.is_arrow_on_horizontal(arrow)) { + expected_y += BubbleBorder::is_arrow_on_top(arrow) + ? kArrowThickness : (-kArrowThickness + kHeightDifference); + } else if (BubbleBorder::has_arrow(arrow)) { + expected_x += BubbleBorder::is_arrow_on_left(arrow) + ? kArrowThickness : (-kArrowThickness + kWidthDifference); + } + EXPECT_EQ(expected_x, origin.x()); + EXPECT_EQ(expected_y, origin.y()); + + border.set_paint_arrow(BubbleBorder::PAINT_NONE); + origin = border.GetBounds(kAnchor, kContentSize).origin(); + expected_x = cases[i].expected_x; + expected_y = cases[i].expected_y; + if (border.is_arrow_on_horizontal(arrow) && + !BubbleBorder::is_arrow_on_top(arrow)) { + expected_y += kHeightDifference; + } else if (BubbleBorder::has_arrow(arrow) && + !border.is_arrow_on_horizontal(arrow) && + !BubbleBorder::is_arrow_on_left(arrow)) { + expected_x += kWidthDifference; + } + EXPECT_EQ(expected_x, origin.x()); + EXPECT_EQ(expected_y, origin.y()); } } diff --git a/chromium/ui/views/bubble/bubble_delegate.cc b/chromium/ui/views/bubble/bubble_delegate.cc index fd922e44547..5850c3f827a 100644 --- a/chromium/ui/views/bubble/bubble_delegate.cc +++ b/chromium/ui/views/bubble/bubble_delegate.cc @@ -6,12 +6,14 @@ #include "build/build_config.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/base/default_style.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/geometry/rect.h" #include "ui/native_theme/native_theme.h" #include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/focus/view_storage.h" +#include "ui/views/layout/layout_constants.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" @@ -19,9 +21,6 @@ #include "ui/base/win/shell.h" #endif -// The defaut margin between the content and the inside border, in pixels. -static const int kDefaultMargin = 6; - namespace views { namespace { @@ -63,7 +62,10 @@ BubbleDelegateView::BubbleDelegateView(View* anchor_view, arrow_(arrow), shadow_(BubbleBorder::SMALL_SHADOW), color_explicitly_set_(false), - margins_(kDefaultMargin, kDefaultMargin, kDefaultMargin, kDefaultMargin), + margins_(kPanelVertMargin, + kPanelHorizMargin, + kPanelVertMargin, + kPanelHorizMargin), accept_events_(true), border_accepts_events_(true), adjust_if_offscreen_(true), @@ -119,10 +121,14 @@ View* BubbleDelegateView::GetContentsView() { NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView( Widget* widget) { - BubbleFrameView* frame = new BubbleFrameView(margins()); + BubbleFrameView* frame = new BubbleFrameView( + gfx::Insets(kPanelVertMargin, kPanelHorizMargin, 0, kPanelHorizMargin), + margins()); // Note: In CreateBubble, the call to SizeToContents() will cause // the relayout that this call requires. frame->SetTitleFontList(GetTitleFontList()); + frame->SetFootnoteView(CreateFootnoteView()); + BubbleBorder::Arrow adjusted_arrow = arrow(); if (base::i18n::IsRTL()) adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow); @@ -201,6 +207,15 @@ void BubbleDelegateView::OnBeforeBubbleWidgetInit(Widget::InitParams* params, Widget* widget) const { } +View* BubbleDelegateView::CreateFootnoteView() { + return nullptr; +} + +void BubbleDelegateView::UseCompactMargins() { + const int kCompactMargin = 6; + margins_.Set(kCompactMargin, kCompactMargin, kCompactMargin, kCompactMargin); +} + void BubbleDelegateView::SetAlignment(BubbleBorder::BubbleAlignment alignment) { GetBubbleFrameView()->bubble_border()->set_alignment(alignment); SizeToContents(); @@ -287,10 +302,9 @@ gfx::Rect BubbleDelegateView::GetBubbleBounds() { const gfx::FontList& BubbleDelegateView::GetTitleFontList() const { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - return rb.GetFontList(ui::ResourceBundle::MediumFont); + return rb.GetFontListWithDelta(ui::kTitleFontSizeDelta); } - void BubbleDelegateView::UpdateColorsFromTheme(const ui::NativeTheme* theme) { if (!color_explicitly_set_) color_ = theme->GetSystemColor(ui::NativeTheme::kColorId_BubbleBackground); @@ -303,10 +317,7 @@ void BubbleDelegateView::UpdateColorsFromTheme(const ui::NativeTheme* theme) { void BubbleDelegateView::HandleVisibilityChanged(Widget* widget, bool visible) { if (widget == GetWidget() && anchor_widget() && anchor_widget()->GetTopLevelWidget()) { - if (visible) - anchor_widget()->GetTopLevelWidget()->DisableInactiveRendering(); - else - anchor_widget()->GetTopLevelWidget()->EnableInactiveRendering(); + anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible); } // Fire AX_EVENT_ALERT for bubbles marked as AX_ROLE_ALERT_DIALOG; this diff --git a/chromium/ui/views/bubble/bubble_delegate.h b/chromium/ui/views/bubble/bubble_delegate.h index 4bc7f4a9d63..9392251e22d 100644 --- a/chromium/ui/views/bubble/bubble_delegate.h +++ b/chromium/ui/views/bubble/bubble_delegate.h @@ -23,6 +23,7 @@ class BubbleFrameView; // BubbleDelegateView creates frame and client views for bubble Widgets. // BubbleDelegateView itself is the client's contents view. +// TODO(estade): remove this in favor of BubbleDialogDelegateView. class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, public WidgetObserver { public: @@ -111,6 +112,12 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, virtual void OnBeforeBubbleWidgetInit(Widget::InitParams* params, Widget* widget) const; + // Creates and returns a view to be displayed at the bottom of the bubble. + virtual View* CreateFootnoteView(); + + // Sets |margins_| to a default picked for smaller bubbles. + void UseCompactMargins(); + // Sets the bubble alignment relative to the anchor. This may only be called // after calling CreateBubble. void SetAlignment(BubbleBorder::BubbleAlignment alignment); diff --git a/chromium/ui/views/bubble/bubble_delegate_unittest.cc b/chromium/ui/views/bubble/bubble_delegate_unittest.cc index 92201c0882a..f7656f06151 100644 --- a/chromium/ui/views/bubble/bubble_delegate_unittest.cc +++ b/chromium/ui/views/bubble/bubble_delegate_unittest.cc @@ -277,6 +277,7 @@ TEST_F(BubbleDelegateTest, CloseReasons) { anchor_widget->GetContentsView(), BubbleBorder::NONE); bubble_delegate->set_close_on_deactivate(true); Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); + anchor_widget->Show(); bubble_widget->Show(); anchor_widget->Activate(); EXPECT_TRUE(bubble_widget->IsClosed()); diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.cc b/chromium/ui/views/bubble/bubble_dialog_delegate.cc new file mode 100644 index 00000000000..9673891dc39 --- /dev/null +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.cc @@ -0,0 +1,319 @@ +// 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/views/bubble/bubble_dialog_delegate.h" + +#include "build/build_config.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/color_utils.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/native_theme/native_theme.h" +#include "ui/views/bubble/bubble_frame_view.h" +#include "ui/views/focus/view_storage.h" +#include "ui/views/layout/layout_constants.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" +#include "ui/views/window/dialog_client_view.h" + +#if defined(OS_WIN) +#include "ui/base/win/shell.h" +#endif + +namespace views { + +namespace { + +// Create a widget to host the bubble. +Widget* CreateBubbleWidget(BubbleDialogDelegateView* bubble) { + Widget* bubble_widget = new Widget(); + Widget::InitParams bubble_params(Widget::InitParams::TYPE_BUBBLE); + bubble_params.delegate = bubble; + bubble_params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; + bubble_params.accept_events = bubble->accept_events(); + if (bubble->parent_window()) + bubble_params.parent = bubble->parent_window(); + else if (bubble->anchor_widget()) + bubble_params.parent = bubble->anchor_widget()->GetNativeView(); + bubble_params.activatable = bubble->CanActivate() + ? Widget::InitParams::ACTIVATABLE_YES + : Widget::InitParams::ACTIVATABLE_NO; + bubble->OnBeforeBubbleWidgetInit(&bubble_params, bubble_widget); + bubble_widget->Init(bubble_params); + if (bubble_params.parent) + bubble_widget->StackAbove(bubble_params.parent); + return bubble_widget; +} + +} // namespace + +// static +const char BubbleDialogDelegateView::kViewClassName[] = + "BubbleDialogDelegateView"; + +BubbleDialogDelegateView::~BubbleDialogDelegateView() { + if (GetWidget()) + GetWidget()->RemoveObserver(this); + SetLayoutManager(NULL); + SetAnchorView(NULL); +} + +// static +Widget* BubbleDialogDelegateView::CreateBubble( + BubbleDialogDelegateView* bubble_delegate) { + bubble_delegate->Init(); + // Get the latest anchor widget from the anchor view at bubble creation time. + bubble_delegate->SetAnchorView(bubble_delegate->GetAnchorView()); + Widget* bubble_widget = CreateBubbleWidget(bubble_delegate); + +#if defined(OS_WIN) + // If glass is enabled, the bubble is allowed to extend outside the bounds of + // the parent frame and let DWM handle compositing. If not, then we don't + // want to allow the bubble to extend the frame because it will be clipped. + bubble_delegate->set_adjust_if_offscreen(ui::win::IsAeroGlassEnabled()); +#elif (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX) + // Linux clips bubble windows that extend outside their parent window bounds. + // Mac never adjusts. + bubble_delegate->set_adjust_if_offscreen(false); +#endif + + bubble_delegate->SizeToContents(); + bubble_widget->AddObserver(bubble_delegate); + return bubble_widget; +} + +BubbleDialogDelegateView* BubbleDialogDelegateView::AsBubbleDialogDelegate() { + return this; +} + +bool BubbleDialogDelegateView::ShouldShowCloseButton() const { + return false; +} + +ClientView* BubbleDialogDelegateView::CreateClientView(Widget* widget) { + DialogClientView* client = new DialogClientView(widget, GetContentsView()); + client->set_button_row_insets(gfx::Insets()); + return client; +} + +NonClientFrameView* BubbleDialogDelegateView::CreateNonClientFrameView( + Widget* widget) { + BubbleFrameView* frame = new BubbleFrameView( + gfx::Insets(kPanelVertMargin, kPanelHorizMargin, 0, kPanelHorizMargin), + margins()); + // Note: In CreateBubble, the call to SizeToContents() will cause + // the relayout that this call requires. + frame->SetTitleFontList(GetTitleFontList()); + frame->SetFootnoteView(CreateFootnoteView()); + + BubbleBorder::Arrow adjusted_arrow = arrow(); + if (base::i18n::IsRTL()) + adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow); + frame->SetBubbleBorder(scoped_ptr<BubbleBorder>( + new BubbleBorder(adjusted_arrow, shadow(), color()))); + return frame; +} + +void BubbleDialogDelegateView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_DIALOG; +} + +const char* BubbleDialogDelegateView::GetClassName() const { + return kViewClassName; +} + +void BubbleDialogDelegateView::OnWidgetDestroying(Widget* widget) { + if (anchor_widget() == widget) + SetAnchorView(NULL); +} + +void BubbleDialogDelegateView::OnWidgetVisibilityChanging(Widget* widget, + bool visible) { +#if defined(OS_WIN) + // On Windows we need to handle this before the bubble is visible or hidden. + // Please see the comment on the OnWidgetVisibilityChanging function. On + // other platforms it is fine to handle it after the bubble is shown/hidden. + HandleVisibilityChanged(widget, visible); +#endif +} + +void BubbleDialogDelegateView::OnWidgetVisibilityChanged(Widget* widget, + bool visible) { +#if !defined(OS_WIN) + HandleVisibilityChanged(widget, visible); +#endif +} + +void BubbleDialogDelegateView::OnWidgetActivationChanged(Widget* widget, + bool active) { + if (close_on_deactivate() && widget == GetWidget() && !active) + GetWidget()->Close(); +} + +void BubbleDialogDelegateView::OnWidgetBoundsChanged( + Widget* widget, + const gfx::Rect& new_bounds) { + if (GetBubbleFrameView() && anchor_widget() == widget) + SizeToContents(); +} + +View* BubbleDialogDelegateView::GetAnchorView() const { + return ViewStorage::GetInstance()->RetrieveView(anchor_view_storage_id_); +} + +gfx::Rect BubbleDialogDelegateView::GetAnchorRect() const { + if (!GetAnchorView()) + return anchor_rect_; + + anchor_rect_ = GetAnchorView()->GetBoundsInScreen(); + anchor_rect_.Inset(anchor_view_insets_); + return anchor_rect_; +} + +void BubbleDialogDelegateView::OnBeforeBubbleWidgetInit( + Widget::InitParams* params, + Widget* widget) const {} + +void BubbleDialogDelegateView::UseCompactMargins() { + const int kCompactMargin = 6; + margins_.Set(kCompactMargin, kCompactMargin, kCompactMargin, kCompactMargin); +} + +void BubbleDialogDelegateView::SetAlignment( + BubbleBorder::BubbleAlignment alignment) { + GetBubbleFrameView()->bubble_border()->set_alignment(alignment); + SizeToContents(); +} + +void BubbleDialogDelegateView::SetArrowPaintType( + BubbleBorder::ArrowPaintType paint_type) { + GetBubbleFrameView()->bubble_border()->set_paint_arrow(paint_type); + SizeToContents(); +} + +void BubbleDialogDelegateView::OnAnchorBoundsChanged() { + SizeToContents(); +} + +BubbleDialogDelegateView::BubbleDialogDelegateView() + : BubbleDialogDelegateView(nullptr, BubbleBorder::TOP_LEFT) {} + +BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view, + BubbleBorder::Arrow arrow) + : close_on_deactivate_(true), + anchor_view_storage_id_(ViewStorage::GetInstance()->CreateStorageID()), + anchor_widget_(NULL), + arrow_(arrow), + shadow_(BubbleBorder::SMALL_SHADOW), + color_explicitly_set_(false), + margins_(kPanelVertMargin, + kPanelHorizMargin, + kPanelVertMargin, + kPanelHorizMargin), + accept_events_(true), + border_accepts_events_(true), + adjust_if_offscreen_(true), + parent_window_(NULL) { + if (anchor_view) + SetAnchorView(anchor_view); + UpdateColorsFromTheme(GetNativeTheme()); +} + +gfx::Rect BubbleDialogDelegateView::GetBubbleBounds() { + // The argument rect has its origin at the bubble's arrow anchor point; + // its size is the preferred size of the bubble's client view (this view). + bool anchor_minimized = anchor_widget() && anchor_widget()->IsMinimized(); + return GetBubbleFrameView()->GetUpdatedWindowBounds( + GetAnchorRect(), GetWidget()->client_view()->GetPreferredSize(), + adjust_if_offscreen_ && !anchor_minimized); +} + +const gfx::FontList& BubbleDialogDelegateView::GetTitleFontList() const { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return rb.GetFontList(ui::ResourceBundle::MediumFont); +} + +void BubbleDialogDelegateView::OnNativeThemeChanged( + const ui::NativeTheme* theme) { + UpdateColorsFromTheme(theme); +} + +void BubbleDialogDelegateView::Init() {} + +void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) { + // When the anchor view gets set the associated anchor widget might + // change as well. + if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) { + if (anchor_widget()) { + anchor_widget_->RemoveObserver(this); + anchor_widget_ = NULL; + } + if (anchor_view) { + anchor_widget_ = anchor_view->GetWidget(); + if (anchor_widget_) + anchor_widget_->AddObserver(this); + } + } + + // Remove the old storage item and set the new (if there is one). + ViewStorage* view_storage = ViewStorage::GetInstance(); + if (view_storage->RetrieveView(anchor_view_storage_id_)) + view_storage->RemoveView(anchor_view_storage_id_); + if (anchor_view) + view_storage->StoreView(anchor_view_storage_id_, anchor_view); + + // Do not update anchoring for NULL views; this could indicate that our + // NativeWindow is being destroyed, so it would be dangerous for us to update + // our anchor bounds at that point. (It's safe to skip this, since if we were + // to update the bounds when |anchor_view| is NULL, the bubble won't move.) + if (anchor_view && GetWidget()) + OnAnchorBoundsChanged(); +} + +void BubbleDialogDelegateView::SetAnchorRect(const gfx::Rect& rect) { + anchor_rect_ = rect; + if (GetWidget()) + OnAnchorBoundsChanged(); +} + +void BubbleDialogDelegateView::SizeToContents() { + GetWidget()->SetBounds(GetBubbleBounds()); +} + +BubbleFrameView* BubbleDialogDelegateView::GetBubbleFrameView() const { + const NonClientView* view = + GetWidget() ? GetWidget()->non_client_view() : NULL; + return view ? static_cast<BubbleFrameView*>(view->frame_view()) : NULL; +} + +void BubbleDialogDelegateView::UpdateColorsFromTheme( + const ui::NativeTheme* theme) { + if (!color_explicitly_set_) + color_ = theme->GetSystemColor(ui::NativeTheme::kColorId_BubbleBackground); + set_background(Background::CreateSolidBackground(color())); + BubbleFrameView* frame_view = GetBubbleFrameView(); + if (frame_view) + frame_view->bubble_border()->set_background_color(color()); +} + +void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget, + bool visible) { + if (widget == GetWidget() && anchor_widget() && + anchor_widget()->GetTopLevelWidget()) { + anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible); + } + + // Fire AX_EVENT_ALERT for bubbles marked as AX_ROLE_ALERT_DIALOG; this + // instructs accessibility tools to read the bubble in its entirety rather + // than just its title and initially focused view. See + // http://crbug.com/474622 for details. + if (widget == GetWidget() && visible) { + ui::AXViewState state; + GetAccessibleState(&state); + if (state.role == ui::AX_ROLE_ALERT_DIALOG) + NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); + } +} + +} // namespace views diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.h b/chromium/ui/views/bubble/bubble_dialog_delegate.h new file mode 100644 index 00000000000..13c576efd7e --- /dev/null +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.h @@ -0,0 +1,205 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_H_ +#define UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_H_ + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "ui/views/bubble/bubble_border.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" +#include "ui/views/window/dialog_delegate.h" + +namespace gfx { +class FontList; +class Rect; +} + +namespace views { + +class BubbleFrameView; + +// BubbleDialogDelegateView is a special DialogDelegateView for bubbles. +class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, + public WidgetObserver { + public: + // Internal class name. + static const char kViewClassName[]; + + enum class CloseReason { + DEACTIVATION, + CLOSE_BUTTON, + UNKNOWN, + }; + + ~BubbleDialogDelegateView() override; + + // Create and initialize the bubble Widget(s) with proper bounds. + static Widget* CreateBubble(BubbleDialogDelegateView* bubble_delegate); + + // WidgetDelegateView overrides: + BubbleDialogDelegateView* AsBubbleDialogDelegate() override; + bool ShouldShowCloseButton() const override; + ClientView* CreateClientView(Widget* widget) override; + NonClientFrameView* CreateNonClientFrameView(Widget* widget) override; + void GetAccessibleState(ui::AXViewState* state) override; + const char* GetClassName() const override; + + // WidgetObserver overrides: + void OnWidgetDestroying(Widget* widget) override; + void OnWidgetVisibilityChanging(Widget* widget, bool visible) override; + void OnWidgetVisibilityChanged(Widget* widget, bool visible) override; + void OnWidgetActivationChanged(Widget* widget, bool active) override; + void OnWidgetBoundsChanged(Widget* widget, + const gfx::Rect& new_bounds) override; + + bool close_on_deactivate() const { return close_on_deactivate_; } + void set_close_on_deactivate(bool close) { close_on_deactivate_ = close; } + + View* GetAnchorView() const; + Widget* anchor_widget() const { return anchor_widget_; } + + // The anchor rect is used in the absence of an assigned anchor view. + const gfx::Rect& anchor_rect() const { return anchor_rect_; } + + BubbleBorder::Arrow arrow() const { return arrow_; } + void set_arrow(BubbleBorder::Arrow arrow) { arrow_ = arrow; } + + BubbleBorder::Shadow shadow() const { return shadow_; } + void set_shadow(BubbleBorder::Shadow shadow) { shadow_ = shadow; } + + SkColor color() const { return color_; } + void set_color(SkColor color) { + color_ = color; + color_explicitly_set_ = true; + } + + const gfx::Insets& margins() const { return margins_; } + void set_margins(const gfx::Insets& margins) { margins_ = margins; } + + const gfx::Insets& anchor_view_insets() const { return anchor_view_insets_; } + void set_anchor_view_insets(const gfx::Insets& i) { anchor_view_insets_ = i; } + + gfx::NativeView parent_window() const { return parent_window_; } + void set_parent_window(gfx::NativeView window) { parent_window_ = window; } + + bool accept_events() const { return accept_events_; } + void set_accept_events(bool accept_events) { accept_events_ = accept_events; } + + bool border_accepts_events() const { return border_accepts_events_; } + void set_border_accepts_events(bool event) { border_accepts_events_ = event; } + + bool adjust_if_offscreen() const { return adjust_if_offscreen_; } + void set_adjust_if_offscreen(bool adjust) { adjust_if_offscreen_ = adjust; } + + // Get the arrow's anchor rect in screen space. + virtual gfx::Rect GetAnchorRect() const; + + // Allows delegates to provide custom parameters before widget initialization. + virtual void OnBeforeBubbleWidgetInit(Widget::InitParams* params, + Widget* widget) const; + + // Sets |margins_| to a default picked for smaller bubbles. + void UseCompactMargins(); + + // Sets the bubble alignment relative to the anchor. This may only be called + // after calling CreateBubble. + void SetAlignment(BubbleBorder::BubbleAlignment alignment); + + // Sets the bubble arrow paint type. + void SetArrowPaintType(BubbleBorder::ArrowPaintType paint_type); + + // Call this method when the anchor bounds have changed to reposition the + // bubble. The bubble is automatically repositioned when the anchor view + // bounds change as a result of the widget's bounds changing. + void OnAnchorBoundsChanged(); + + protected: + BubbleDialogDelegateView(); + BubbleDialogDelegateView(View* anchor_view, BubbleBorder::Arrow arrow); + + // Get bubble bounds from the anchor rect and client view's preferred size. + virtual gfx::Rect GetBubbleBounds(); + + // Return a FontList to use for the title of the bubble. + // (The default is MediumFont). + virtual const gfx::FontList& GetTitleFontList() const; + + // View overrides: + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + + // Perform view initialization on the contents for bubble sizing. + virtual void Init(); + + // Sets the anchor view or rect and repositions the bubble. Note that if a + // valid view gets passed, the anchor rect will get ignored. If the view gets + // deleted, but no new view gets set, the last known anchor postion will get + // returned. + void SetAnchorView(View* anchor_view); + void SetAnchorRect(const gfx::Rect& rect); + + // Resize and potentially move the bubble to fit the content's preferred size. + void SizeToContents(); + + BubbleFrameView* GetBubbleFrameView() const; + + private: + friend class BubbleBorderDelegate; + friend class BubbleWindowTargeter; + + FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CreateDelegate); + FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, NonClientHitTest); + + // Update the bubble color from |theme|, unless it was explicitly set. + void UpdateColorsFromTheme(const ui::NativeTheme* theme); + + // Handles widget visibility changes. + void HandleVisibilityChanged(Widget* widget, bool visible); + + // A flag controlling bubble closure on deactivation. + bool close_on_deactivate_; + + // The view and widget to which this bubble is anchored. Since an anchor view + // can be deleted without notice, we store it in the ViewStorage and retrieve + // it from there. It will make sure that the view is still valid. + const int anchor_view_storage_id_; + Widget* anchor_widget_; + + // The anchor rect used in the absence of an anchor view. + mutable gfx::Rect anchor_rect_; + + // The arrow's location on the bubble. + BubbleBorder::Arrow arrow_; + + // Bubble border shadow to use. + BubbleBorder::Shadow shadow_; + + // The background color of the bubble; and flag for when it's explicitly set. + SkColor color_; + bool color_explicitly_set_; + + // The margins between the content and the inside of the border. + gfx::Insets margins_; + + // Insets applied to the |anchor_view_| bounds. + gfx::Insets anchor_view_insets_; + + // Specifies whether the bubble (or its border) handles mouse events, etc. + bool accept_events_; + bool border_accepts_events_; + + // If true (defaults to true), the arrow may be mirrored and moved to fit the + // bubble on screen better. It would be a no-op if the bubble has no arrow. + bool adjust_if_offscreen_; + + // Parent native window of the bubble. + gfx::NativeView parent_window_; + + DISALLOW_COPY_AND_ASSIGN(BubbleDialogDelegateView); +}; + +} // namespace views + +#endif // UI_VIEWS_BUBBLE_BUBBLE_DELEGATE2_H_ diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc new file mode 100644 index 00000000000..6c43388c944 --- /dev/null +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc @@ -0,0 +1,316 @@ +// 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/views/bubble/bubble_dialog_delegate.h" + +#include <stddef.h> + +#include "base/macros.h" +#include "ui/base/hit_test.h" +#include "ui/events/event_utils.h" +#include "ui/views/bubble/bubble_frame_view.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/test/test_widget_observer.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" + +namespace views { + +namespace { + +class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { + public: + TestBubbleDialogDelegateView(View* anchor_view) + : BubbleDialogDelegateView(anchor_view, BubbleBorder::TOP_LEFT), + view_(new View()) { + view_->SetFocusable(true); + AddChildView(view_); + } + ~TestBubbleDialogDelegateView() override {} + + // BubbleDialogDelegateView overrides: + View* GetInitiallyFocusedView() override { return view_; } + gfx::Size GetPreferredSize() const override { return gfx::Size(200, 200); } + + using BubbleDialogDelegateView::SetAnchorRect; + using BubbleDialogDelegateView::GetBubbleFrameView; + + private: + View* view_; + + DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView); +}; + +class BubbleDialogDelegateTest : public ViewsTestBase { + public: + BubbleDialogDelegateTest() {} + ~BubbleDialogDelegateTest() override {} + + // Creates a test widget that owns its native widget. + Widget* CreateTestWidget() { + Widget* widget = new Widget(); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + return widget; + } + + private: + DISALLOW_COPY_AND_ASSIGN(BubbleDialogDelegateTest); +}; + +} // namespace + +TEST_F(BubbleDialogDelegateTest, CreateDelegate) { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + TestBubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + bubble_delegate->set_color(SK_ColorGREEN); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate()); + EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget()); + test::TestWidgetObserver bubble_observer(bubble_widget); + bubble_widget->Show(); + + BubbleBorder* border = bubble_delegate->GetBubbleFrameView()->bubble_border(); + EXPECT_EQ(bubble_delegate->arrow(), border->arrow()); + EXPECT_EQ(bubble_delegate->color(), border->background_color()); + + EXPECT_FALSE(bubble_observer.widget_closed()); + bubble_widget->CloseNow(); + EXPECT_TRUE(bubble_observer.widget_closed()); +} + +TEST_F(BubbleDialogDelegateTest, CloseAnchorWidget) { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + BubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + // Preventing close on deactivate should not prevent closing with the anchor. + bubble_delegate->set_close_on_deactivate(false); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate()); + EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget()); + EXPECT_EQ(anchor_widget.get(), bubble_delegate->anchor_widget()); + test::TestWidgetObserver bubble_observer(bubble_widget); + EXPECT_FALSE(bubble_observer.widget_closed()); + + bubble_widget->Show(); + EXPECT_EQ(anchor_widget.get(), bubble_delegate->anchor_widget()); + EXPECT_FALSE(bubble_observer.widget_closed()); + + // TODO(msw): Remove activation hack to prevent bookkeeping errors in: + // aura::test::TestActivationClient::OnWindowDestroyed(). + scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget()); + EXPECT_FALSE(bubble_observer.widget_closed()); + + // Ensure that closing the anchor widget also closes the bubble itself. + anchor_widget->CloseNow(); + EXPECT_TRUE(bubble_observer.widget_closed()); +} + +// This test checks that the bubble delegate is capable to handle an early +// destruction of the used anchor view. (Animations and delayed closure of the +// bubble will call upon the anchor view to get its location). +TEST_F(BubbleDialogDelegateTest, CloseAnchorViewTest) { + // Create an anchor widget and add a view to be used as an anchor view. + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + scoped_ptr<View> anchor_view(new View()); + anchor_widget->GetContentsView()->AddChildView(anchor_view.get()); + TestBubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_view.get()); + // Prevent flakes by avoiding closing on activation changes. + bubble_delegate->set_close_on_deactivate(false); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + + // Check that the anchor view is correct and set up an anchor view rect. + // Make sure that this rect will get ignored (as long as the anchor view is + // attached). + EXPECT_EQ(anchor_view.get(), bubble_delegate->GetAnchorView()); + const gfx::Rect set_anchor_rect = gfx::Rect(10, 10, 100, 100); + bubble_delegate->SetAnchorRect(set_anchor_rect); + const gfx::Rect view_rect = bubble_delegate->GetAnchorRect(); + EXPECT_NE(view_rect.ToString(), set_anchor_rect.ToString()); + + // Create the bubble. + bubble_widget->Show(); + EXPECT_EQ(anchor_widget.get(), bubble_delegate->anchor_widget()); + + // Remove now the anchor view and make sure that the original found rect + // is still kept, so that the bubble does not jump when the view gets deleted. + anchor_widget->GetContentsView()->RemoveChildView(anchor_view.get()); + anchor_view.reset(); + EXPECT_EQ(NULL, bubble_delegate->GetAnchorView()); + EXPECT_EQ(view_rect.ToString(), bubble_delegate->GetAnchorRect().ToString()); +} + +// Testing that a move of the anchor view will lead to new bubble locations. +TEST_F(BubbleDialogDelegateTest, TestAnchorRectMovesWithViewTest) { + // Create an anchor widget and add a view to be used as anchor view. + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + TestBubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + + anchor_widget->GetContentsView()->SetBounds(10, 10, 100, 100); + const gfx::Rect view_rect = bubble_delegate->GetAnchorRect(); + + anchor_widget->GetContentsView()->SetBounds(20, 10, 100, 100); + const gfx::Rect view_rect_2 = bubble_delegate->GetAnchorRect(); + EXPECT_NE(view_rect.ToString(), view_rect_2.ToString()); +} + +TEST_F(BubbleDialogDelegateTest, ResetAnchorWidget) { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + BubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + + // Make sure the bubble widget is parented to a widget other than the anchor + // widget so that closing the anchor widget does not close the bubble widget. + scoped_ptr<Widget> parent_widget(CreateTestWidget()); + bubble_delegate->set_parent_window(parent_widget->GetNativeView()); + // Preventing close on deactivate should not prevent closing with the parent. + bubble_delegate->set_close_on_deactivate(false); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate()); + EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget()); + EXPECT_EQ(anchor_widget.get(), bubble_delegate->anchor_widget()); + test::TestWidgetObserver bubble_observer(bubble_widget); + EXPECT_FALSE(bubble_observer.widget_closed()); + + // Showing and hiding the bubble widget should have no effect on its anchor. + bubble_widget->Show(); + EXPECT_EQ(anchor_widget.get(), bubble_delegate->anchor_widget()); + bubble_widget->Hide(); + EXPECT_EQ(anchor_widget.get(), bubble_delegate->anchor_widget()); + + // Ensure that closing the anchor widget clears the bubble's reference to that + // anchor widget, but the bubble itself does not close. + anchor_widget->CloseNow(); + EXPECT_NE(anchor_widget.get(), bubble_delegate->anchor_widget()); + EXPECT_FALSE(bubble_observer.widget_closed()); + + // TODO(msw): Remove activation hack to prevent bookkeeping errors in: + // aura::test::TestActivationClient::OnWindowDestroyed(). + scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget()); + EXPECT_FALSE(bubble_observer.widget_closed()); + + // Ensure that closing the parent widget also closes the bubble itself. + parent_widget->CloseNow(); + EXPECT_TRUE(bubble_observer.widget_closed()); +} + +TEST_F(BubbleDialogDelegateTest, InitiallyFocusedView) { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + BubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + bubble_widget->Show(); + EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(), + bubble_widget->GetFocusManager()->GetFocusedView()); + bubble_widget->CloseNow(); +} + +TEST_F(BubbleDialogDelegateTest, NonClientHitTest) { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + TestBubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + BubbleFrameView* frame = bubble_delegate->GetBubbleFrameView(); + const int border = frame->bubble_border()->GetBorderThickness(); + + struct { + const int point; + const int hit; + } cases[] = { + {border, HTNOWHERE}, {border + 50, HTCLIENT}, {1000, HTNOWHERE}, + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + gfx::Point point(cases[i].point, cases[i].point); + EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point)) + << " with border: " << border << ", at point " << cases[i].point; + } +} + +TEST_F(BubbleDialogDelegateTest, VisibleWhenAnchorWidgetBoundsChanged) { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + BubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + test::TestWidgetObserver bubble_observer(bubble_widget); + EXPECT_FALSE(bubble_observer.widget_closed()); + + anchor_widget->Show(); + bubble_widget->Show(); + EXPECT_TRUE(bubble_widget->IsVisible()); + anchor_widget->SetBounds(gfx::Rect(10, 10, 100, 100)); + EXPECT_TRUE(bubble_widget->IsVisible()); +} + +// Test that setting WidgetDelegate::set_can_activate() to false makes the +// widget created via BubbleDialogDelegateView::CreateBubble() not activatable. +TEST_F(BubbleDialogDelegateTest, NotActivatable) { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + BubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + bubble_delegate->set_can_activate(false); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + bubble_widget->Show(); + EXPECT_FALSE(bubble_widget->CanActivate()); +} + +TEST_F(BubbleDialogDelegateTest, CloseMethods) { + { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + BubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + bubble_delegate->set_close_on_deactivate(true); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + anchor_widget->Show(); + bubble_widget->Show(); + anchor_widget->Activate(); + EXPECT_TRUE(bubble_widget->IsClosed()); + } + + { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + BubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + bubble_widget->Show(); + + ui::KeyEvent escape_event(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, ui::EF_NONE); + bubble_widget->OnKeyEvent(&escape_event); + EXPECT_TRUE(bubble_widget->IsClosed()); + } + + { + scoped_ptr<Widget> anchor_widget(CreateTestWidget()); + TestBubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + bubble_widget->Show(); + BubbleFrameView* frame_view = bubble_delegate->GetBubbleFrameView(); + LabelButton* close_button = frame_view->close_; + ASSERT_TRUE(close_button); + frame_view->ButtonPressed( + close_button, + ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE)); + EXPECT_TRUE(bubble_widget->IsClosed()); + } +} + +} // namespace views diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc index b11ee79c6f1..3f8411b22d8 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.cc +++ b/chromium/ui/views/bubble/bubble_frame_view.cc @@ -8,9 +8,13 @@ #include <utility> #include "build/build_config.h" +#include "ui/base/default_style.h" #include "ui/base/hit_test.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/paint_context.h" +#include "ui/compositor/paint_recorder.h" +#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/path.h" #include "ui/gfx/screen.h" #include "ui/gfx/skia_util.h" @@ -20,21 +24,22 @@ #include "ui/views/bubble/bubble_border.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/image_view.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/layout_constants.h" #include "ui/views/resources/grit/views_resources.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/window/client_view.h" +namespace views { + namespace { -// Insets for the title bar views in pixels. -const int kTitleTopInset = 12; -const int kTitleLeftInset = 19; -const int kTitleBottomInset = 12; -const int kTitleRightInset = 7; +// Background color of the footnote view. +const SkColor kFootnoteBackgroundColor = SkColorSetRGB(245, 245, 245); -// The horizontal padding between the title and the icon. -const int kTitleHorizontalPadding = 5; +// Color of the top border of the footnote. +const SkColor kFootnoteBorderColor = SkColorSetRGB(229, 229, 229); // Get the |vertical| or horizontal amount that |available_bounds| overflows // |window_bounds|. @@ -61,27 +66,28 @@ int GetOffScreenLength(const gfx::Rect& available_bounds, } // namespace -namespace views { - // static const char BubbleFrameView::kViewClassName[] = "BubbleFrameView"; -BubbleFrameView::BubbleFrameView(const gfx::Insets& content_margins) +BubbleFrameView::BubbleFrameView(const gfx::Insets& title_margins, + const gfx::Insets& content_margins) : bubble_border_(nullptr), + title_margins_(title_margins), content_margins_(content_margins), title_icon_(new views::ImageView()), title_(nullptr), close_(nullptr), - titlebar_extra_view_(nullptr), + footnote_container_(nullptr), close_button_clicked_(false) { AddChildView(title_icon_); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); title_ = new Label(base::string16(), - rb.GetFontList(ui::ResourceBundle::MediumFont)); + rb.GetFontListWithDelta(ui::kTitleFontSizeDelta)); title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); title_->set_collapse_when_hidden(true); title_->SetVisible(false); + title_->SetMultiLine(true); AddChildView(title_); close_ = CreateCloseButton(this); @@ -92,12 +98,6 @@ BubbleFrameView::BubbleFrameView(const gfx::Insets& content_margins) BubbleFrameView::~BubbleFrameView() {} // static -gfx::Insets BubbleFrameView::GetTitleInsets() { - return gfx::Insets( - kTitleTopInset, kTitleLeftInset, kTitleBottomInset, kTitleRightInset); -} - -// static LabelButton* BubbleFrameView::CreateCloseButton(ButtonListener* listener) { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); LabelButton* close = new LabelButton(listener, base::string16()); @@ -118,9 +118,12 @@ LabelButton* BubbleFrameView::CreateCloseButton(ButtonListener* listener) { } gfx::Rect BubbleFrameView::GetBoundsForClientView() const { - gfx::Rect client_bounds = GetLocalBounds(); + gfx::Rect client_bounds = GetContentsBounds(); client_bounds.Inset(GetInsets()); - client_bounds.Inset(bubble_border_->GetInsets()); + if (footnote_container_) { + client_bounds.set_height(client_bounds.height() - + footnote_container_->height()); + } return client_bounds; } @@ -130,6 +133,20 @@ gfx::Rect BubbleFrameView::GetWindowBoundsForClientBounds( return bubble_border_->GetBounds(gfx::Rect(), size); } +bool BubbleFrameView::GetClientMask(const gfx::Size& size, + gfx::Path* path) const { + const int radius = bubble_border_->GetBorderCornerRadius(); + gfx::Insets content_insets = GetInsets(); + // If the client bounds don't touch the edges, no need to mask. + if (std::min({content_insets.top(), content_insets.left(), + content_insets.bottom(), content_insets.right()}) > radius) { + return false; + } + gfx::RectF rect((gfx::Rect(size))); + path->addRoundRect(gfx::RectFToSkRect(rect), radius, radius); + return true; +} + int BubbleFrameView::NonClientHitTest(const gfx::Point& point) { if (!bounds().Contains(point)) return HTNOWHERE; @@ -137,8 +154,11 @@ int BubbleFrameView::NonClientHitTest(const gfx::Point& point) { return HTCLOSE; // Allow dialogs to show the system menu and be dragged. - if (GetWidget()->widget_delegate()->AsDialogDelegate()) { - gfx::Rect sys_rect(0, 0, title_->x(), title_->y()); + if (GetWidget()->widget_delegate()->AsDialogDelegate() && + !GetWidget()->widget_delegate()->AsBubbleDialogDelegate()) { + gfx::Rect bounds(GetContentsBounds()); + bounds.Inset(title_margins_); + gfx::Rect sys_rect(0, 0, bounds.x(), bounds.y()); sys_rect.set_origin(gfx::Point(GetMirroredXForRect(sys_rect), 0)); if (sys_rect.Contains(point)) return HTSYSMENU; @@ -151,30 +171,42 @@ int BubbleFrameView::NonClientHitTest(const gfx::Point& point) { void BubbleFrameView::GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) { - // NOTE: this only provides implementations for the types used by dialogs. - if ((bubble_border_->arrow() != BubbleBorder::NONE && - bubble_border_->arrow() != BubbleBorder::FLOAT) || - (bubble_border_->shadow() != BubbleBorder::SMALL_SHADOW && - bubble_border_->shadow() != BubbleBorder::NO_SHADOW_OPAQUE_BORDER)) + if (bubble_border_->shadow() != BubbleBorder::SMALL_SHADOW && + bubble_border_->shadow() != BubbleBorder::NO_SHADOW_OPAQUE_BORDER && + bubble_border_->shadow() != BubbleBorder::NO_ASSETS) + return; + + // We don't return a mask for windows with arrows unless they use + // BubbleBorder::NO_ASSETS. + if (bubble_border_->shadow() != BubbleBorder::NO_ASSETS && + bubble_border_->arrow() != BubbleBorder::NONE && + bubble_border_->arrow() != BubbleBorder::FLOAT) return; // Use a window mask roughly matching the border in the image assets. - static const int kBorderStrokeSize = 1; - static const SkScalar kCornerRadius = SkIntToScalar(6); + const int kBorderStrokeSize = + bubble_border_->shadow() == BubbleBorder::NO_ASSETS ? 0 : 1; + const SkScalar kCornerRadius = + SkIntToScalar(bubble_border_->GetBorderCornerRadius()); const gfx::Insets border_insets = bubble_border_->GetInsets(); - SkRect rect = { SkIntToScalar(border_insets.left() - kBorderStrokeSize), - SkIntToScalar(border_insets.top() - kBorderStrokeSize), - SkIntToScalar(size.width() - border_insets.right() + - kBorderStrokeSize), - SkIntToScalar(size.height() - border_insets.bottom() + - kBorderStrokeSize) }; - if (bubble_border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER) { + SkRect rect = { + SkIntToScalar(border_insets.left() - kBorderStrokeSize), + SkIntToScalar(border_insets.top() - kBorderStrokeSize), + SkIntToScalar(size.width() - border_insets.right() + kBorderStrokeSize), + SkIntToScalar(size.height() - border_insets.bottom() + + kBorderStrokeSize)}; + + if (bubble_border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER || + bubble_border_->shadow() == BubbleBorder::NO_ASSETS) { window_mask->addRoundRect(rect, kCornerRadius, kCornerRadius); } else { static const int kBottomBorderShadowSize = 2; rect.fBottom += SkIntToScalar(kBottomBorderShadowSize); window_mask->addRect(rect); } + gfx::Path arrow_path; + if (bubble_border_->GetArrowPath(gfx::Rect(size), &arrow_path)) + window_mask->addPath(arrow_path, 0, 0); } void BubbleFrameView::ResetWindowControls() { @@ -200,13 +232,17 @@ void BubbleFrameView::SetTitleFontList(const gfx::FontList& font_list) { title_->SetFontList(font_list); } +const char* BubbleFrameView::GetClassName() const { + return kViewClassName; +} + gfx::Insets BubbleFrameView::GetInsets() const { gfx::Insets insets = content_margins_; const int icon_height = title_icon_->GetPreferredSize().height(); const int label_height = title_->GetPreferredSize().height(); const bool has_title = icon_height > 0 || label_height > 0; - const int title_padding = has_title ? kTitleTopInset + kTitleBottomInset : 0; + const int title_padding = has_title ? title_margins_.height() : 0; const int title_height = std::max(icon_height, label_height) + title_padding; const int close_height = close_->visible() ? close_->height() : 0; insets += gfx::Insets(std::max(title_height, close_height), 0, 0, 0); @@ -251,62 +287,47 @@ gfx::Size BubbleFrameView::GetMaximumSize() const { } void BubbleFrameView::Layout() { + // The title margins may not be set, but make sure that's only the case when + // there's no title. + DCHECK(!title_margins_.IsEmpty() || !title_->visible()); + gfx::Rect bounds(GetContentsBounds()); - bounds.Inset(GetTitleInsets()); + bounds.Inset(title_margins_); if (bounds.IsEmpty()) return; - // The close button top inset is actually smaller than the title top inset. - close_->SetPosition(gfx::Point(bounds.right() - close_->width(), - bounds.y() - 5)); + // The close button is positioned somewhat closer to the edge of the bubble. + gfx::Point close_position = GetContentsBounds().top_right(); + close_position += gfx::Vector2d(-close_->width() - 7, 6); + close_->SetPosition(close_position); - gfx::Size title_icon_size(title_icon_->GetPreferredSize()); - gfx::Size title_label_size(title_->GetPreferredSize()); + gfx::Size title_icon_pref_size(title_icon_->GetPreferredSize()); int padding = 0; - if (title_icon_size.width() > 0 && title_label_size.width() > 0) - padding = kTitleHorizontalPadding; - const int title_height = std::max(title_icon_size.height(), - title_label_size.height()); - - const int title_icon_width = std::max(0, close_->x() - bounds.x()); - title_icon_size.SetToMin(gfx::Size(title_icon_width, title_height)); - gfx::Rect title_icon_bounds( - bounds.x(), bounds.y(), title_icon_size.width(), title_height); - title_icon_->SetBoundsRect(title_icon_bounds); - - const int title_label_x = title_icon_->bounds().right() + padding; - const int title_label_width = std::max(0, close_->x() - title_label_x); - title_label_size.SetToMin(gfx::Size(title_label_width, - title_label_size.height())); - gfx::Rect title_label_bounds( - title_label_x, bounds.y(), title_label_size.width(), title_height); - title_->SetBoundsRect(title_label_bounds); - - bounds.set_width( - title_icon_size.width() + title_label_size.width() + padding); - bounds.set_height(title_height); - if (titlebar_extra_view_) { - const int extra_width = close_->x() - bounds.right(); - gfx::Size size = titlebar_extra_view_->GetPreferredSize(); - size.SetToMin(gfx::Size(std::max(0, extra_width), size.height())); - gfx::Rect titlebar_extra_view_bounds( - close_->x() - size.width(), - bounds.y(), - size.width(), - bounds.height()); - titlebar_extra_view_bounds.Subtract(bounds); - titlebar_extra_view_->SetBoundsRect(titlebar_extra_view_bounds); + if (title_->visible() && !title_->text().empty()) { + if (title_icon_pref_size.width() > 0) + padding = title_margins_.left(); + + const int title_label_x = + bounds.x() + title_icon_pref_size.width() + padding; + title_->SizeToFit(std::max(1, close_->x() - title_label_x)); + title_->SetPosition(gfx::Point(title_label_x, bounds.y())); } -} -const char* BubbleFrameView::GetClassName() const { - return kViewClassName; -} + const int title_height = + std::max(title_icon_pref_size.height(), title_->height()); + title_icon_->SetBounds(bounds.x(), bounds.y(), title_icon_pref_size.width(), + title_height); + bounds.set_width(title_->bounds().right() - bounds.x()); + bounds.set_height(title_height); -void BubbleFrameView::ChildPreferredSizeChanged(View* child) { - if (child == titlebar_extra_view_ || child == title_) - Layout(); + if (footnote_container_) { + gfx::Rect local_bounds = GetContentsBounds(); + int height = footnote_container_->GetHeightForWidth(local_bounds.width()); + footnote_container_->SetBounds(local_bounds.x(), + local_bounds.bottom() - height, + local_bounds.width(), height); + } } void BubbleFrameView::OnThemeChanged() { @@ -323,6 +344,19 @@ void BubbleFrameView::OnNativeThemeChanged(const ui::NativeTheme* theme) { } } +void BubbleFrameView::OnPaint(gfx::Canvas* canvas) { + OnPaintBackground(canvas); + // Border comes after children. +} + +void BubbleFrameView::PaintChildren(const ui::PaintContext& context) { + NonClientFrameView::PaintChildren(context); + + ui::PaintCache paint_cache; + ui::PaintRecorder recorder(context, size(), &paint_cache); + OnPaintBorder(recorder.canvas()); +} + void BubbleFrameView::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == close_) { close_button_clicked_ = true; @@ -338,11 +372,21 @@ void BubbleFrameView::SetBubbleBorder(scoped_ptr<BubbleBorder> border) { set_background(new views::BubbleBackground(bubble_border_)); } -void BubbleFrameView::SetTitlebarExtraView(View* view) { - DCHECK(view); - DCHECK(!titlebar_extra_view_); - AddChildView(view); - titlebar_extra_view_ = view; +void BubbleFrameView::SetFootnoteView(View* view) { + if (!view) + return; + + DCHECK(!footnote_container_); + footnote_container_ = new views::View(); + footnote_container_->SetLayoutManager( + new BoxLayout(BoxLayout::kVertical, content_margins_.left(), + content_margins_.top(), 0)); + footnote_container_->set_background( + Background::CreateSolidBackground(kFootnoteBackgroundColor)); + footnote_container_->SetBorder( + Border::CreateSolidSidedBorder(1, 0, 0, 0, kFootnoteBorderColor)); + footnote_container_->AddChildView(view); + AddChildView(footnote_container_); } gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect, @@ -370,9 +414,9 @@ gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect, gfx::Rect BubbleFrameView::GetAvailableScreenBounds( const gfx::Rect& rect) const { // The bubble attempts to fit within the current screen bounds. - // TODO(scottmg): Native is wrong. http://crbug.com/133312 - return gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint( - rect.CenterPoint()).work_area(); + return gfx::Screen::GetScreen() + ->GetDisplayNearestPoint(rect.CenterPoint()) + .work_area(); } bool BubbleFrameView::IsCloseButtonVisible() const { @@ -453,23 +497,23 @@ void BubbleFrameView::OffsetArrowIfOffScreen(const gfx::Rect& anchor_rect, gfx::Size BubbleFrameView::GetSizeForClientSize( const gfx::Size& client_size) const { // Accommodate the width of the title bar elements. - int title_bar_width = GetInsets().width() + border()->GetInsets().width(); + int title_bar_width = title_margins_.width() + border()->GetInsets().width(); gfx::Size title_icon_size = title_icon_->GetPreferredSize(); gfx::Size title_label_size = title_->GetPreferredSize(); - if (title_icon_size.width() > 0 || title_label_size.width() > 0) - title_bar_width += kTitleLeftInset; if (title_icon_size.width() > 0 && title_label_size.width() > 0) - title_bar_width += kTitleHorizontalPadding; + title_bar_width += title_margins_.left(); title_bar_width += title_icon_size.width(); - title_bar_width += title_label_size.width(); if (close_->visible()) title_bar_width += close_->width() + 1; - if (titlebar_extra_view_ != NULL) - title_bar_width += titlebar_extra_view_->GetPreferredSize().width(); + gfx::Size size(client_size); + gfx::Insets client_insets = GetInsets(); + size.Enlarge(client_insets.width(), client_insets.height()); size.SetToMax(gfx::Size(title_bar_width, 0)); - const gfx::Insets insets(GetInsets()); - size.Enlarge(insets.width(), insets.height()); + + if (footnote_container_) + size.Enlarge(0, footnote_container_->GetHeightForWidth(size.width())); + return size; } diff --git a/chromium/ui/views/bubble/bubble_frame_view.h b/chromium/ui/views/bubble/bubble_frame_view.h index 6a4a0f30a5a..812c3e77f35 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.h +++ b/chromium/ui/views/bubble/bubble_frame_view.h @@ -30,13 +30,10 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, // Internal class name. static const char kViewClassName[]; - explicit BubbleFrameView(const gfx::Insets& content_margins); + BubbleFrameView(const gfx::Insets& title_margins, + const gfx::Insets& content_margins); ~BubbleFrameView() override; - // Insets to make bubble contents align horizontal with the bubble title. - // NOTE: this does not take into account whether a title actually exists. - static gfx::Insets GetTitleInsets(); - // Creates a close button used in the corner of the dialog. static LabelButton* CreateCloseButton(ButtonListener* listener); @@ -44,6 +41,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, gfx::Rect GetBoundsForClientView() const override; gfx::Rect GetWindowBoundsForClientBounds( const gfx::Rect& client_bounds) const override; + bool GetClientMask(const gfx::Size& size, gfx::Path* path) const override; int NonClientHitTest(const gfx::Point& point) override; void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override; void ResetWindowControls() override; @@ -56,13 +54,14 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, void SetTitleFontList(const gfx::FontList& font_list); // View overrides: + const char* GetClassName() const override; gfx::Insets GetInsets() const override; gfx::Size GetPreferredSize() const override; gfx::Size GetMinimumSize() const override; gfx::Size GetMaximumSize() const override; void Layout() override; - const char* GetClassName() const override; - void ChildPreferredSizeChanged(View* child) override; + void OnPaint(gfx::Canvas* canvas) override; + void PaintChildren(const ui::PaintContext& context) override; void OnThemeChanged() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; @@ -75,7 +74,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, gfx::Insets content_margins() const { return content_margins_; } - void SetTitlebarExtraView(View* view); + void SetFootnoteView(View* view); // 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 @@ -96,6 +95,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, private: FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, GetBoundsForClientView); FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CloseReasons); + FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateTest, CloseMethods); // Mirrors the bubble's arrow location on the |vertical| or horizontal axis, // if the generated window bounds don't fit in the monitor bounds. @@ -114,6 +114,9 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, // The bubble border. BubbleBorder* bubble_border_; + // Margins around the title label. + gfx::Insets title_margins_; + // Margins between the content and the inside of the border, in pixels. gfx::Insets content_margins_; @@ -122,9 +125,8 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, Label* title_; LabelButton* close_; - // When supplied, this view is placed in the titlebar between the title and - // (x) close button. - View* titlebar_extra_view_; + // A view to contain the footnote view, if it exists. + View* footnote_container_; // Whether the close button was clicked. bool close_button_clicked_; diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc index 908b190340e..d71137f5eb2 100644 --- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc +++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc @@ -30,8 +30,11 @@ const int kMaximumClientWidth = 300; const int kMaximumClientHeight = 300; const int kPreferredClientWidth = 150; const int kPreferredClientHeight = 250; -const int kExpectedBorderWidth = 22; -const int kExpectedBorderHeight = 29; + +// These account for non-client areas like the title bar, footnote etc. However +// these do not take the bubble border into consideration. +const int kExpectedAdditionalWidth = 12; +const int kExpectedAdditionalHeight = 12; class TestBubbleFrameViewWidgetDelegate : public WidgetDelegate { public: @@ -64,7 +67,7 @@ class TestBubbleFrameViewWidgetDelegate : public WidgetDelegate { class TestBubbleFrameView : public BubbleFrameView { public: TestBubbleFrameView(ViewsTestBase* test_base) - : BubbleFrameView(gfx::Insets(kMargin, kMargin, kMargin, kMargin)), + : BubbleFrameView(gfx::Insets(), gfx::Insets(kMargin)), test_base_(test_base), available_bounds_(gfx::Rect(0, 0, 1000, 1000)) { SetBubbleBorder(scoped_ptr<BubbleBorder>( @@ -417,36 +420,40 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) { TEST_F(BubbleFrameViewTest, GetPreferredSize) { TestBubbleFrameView frame(this); - gfx::Size preferred_size = frame.GetPreferredSize(); + gfx::Rect preferred_rect(frame.GetPreferredSize()); // Expect that a border has been added to the preferred size. - EXPECT_EQ(kPreferredClientWidth + kExpectedBorderWidth, - preferred_size.width()); - EXPECT_EQ(kPreferredClientHeight + kExpectedBorderHeight, - preferred_size.height()); + preferred_rect.Inset(frame.bubble_border()->GetInsets()); + + gfx::Size expected_size(kPreferredClientWidth + kExpectedAdditionalWidth, + kPreferredClientHeight + kExpectedAdditionalHeight); + EXPECT_EQ(expected_size, preferred_rect.size()); } TEST_F(BubbleFrameViewTest, GetMinimumSize) { TestBubbleFrameView frame(this); - gfx::Size minimum_size = frame.GetMinimumSize(); + gfx::Rect minimum_rect(frame.GetMinimumSize()); // Expect that a border has been added to the minimum size. - EXPECT_EQ(kMinimumClientWidth + kExpectedBorderWidth, minimum_size.width()); - EXPECT_EQ(kMinimumClientHeight + kExpectedBorderHeight, - minimum_size.height()); + minimum_rect.Inset(frame.bubble_border()->GetInsets()); + + gfx::Size expected_size(kMinimumClientWidth + kExpectedAdditionalWidth, + kMinimumClientHeight + kExpectedAdditionalHeight); + EXPECT_EQ(expected_size, minimum_rect.size()); } TEST_F(BubbleFrameViewTest, GetMaximumSize) { TestBubbleFrameView frame(this); - gfx::Size maximum_size = frame.GetMaximumSize(); + gfx::Rect maximum_rect(frame.GetMaximumSize()); #if defined(OS_WIN) // On Windows, GetMaximumSize causes problems with DWM, so it should just be 0 // (unlimited). See http://crbug.com/506206. - EXPECT_EQ(0, maximum_size.width()); - EXPECT_EQ(0, maximum_size.height()); + EXPECT_EQ(gfx::Size(), maximum_rect.size()); #else + maximum_rect.Inset(frame.bubble_border()->GetInsets()); + // Should ignore the contents view's maximum size and use the preferred size. - EXPECT_EQ(kPreferredClientWidth + kExpectedBorderWidth, maximum_size.width()); - EXPECT_EQ(kPreferredClientHeight + kExpectedBorderHeight, - maximum_size.height()); + gfx::Size expected_size(kPreferredClientWidth + kExpectedAdditionalWidth, + kPreferredClientHeight + kExpectedAdditionalHeight); + EXPECT_EQ(expected_size, maximum_rect.size()); #endif } diff --git a/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc b/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc index 8ae398f4083..f40aa326977 100644 --- a/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc +++ b/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc @@ -100,7 +100,7 @@ TEST_F(BubbleWindowTargeterTest, HitTest) { EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1)); } { - bubble_delegate()->set_margins(gfx::Insets(20, 20, 20, 20)); + 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); @@ -110,7 +110,7 @@ TEST_F(BubbleWindowTargeterTest, HitTest) { bubble_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( new BubbleWindowTargeter(bubble_delegate()))); { - bubble_delegate()->set_margins(gfx::Insets(20, 20, 20, 20)); + 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); diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc index daa24f86811..ad3267fe659 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.cc +++ b/chromium/ui/views/bubble/tray_bubble_view.cc @@ -290,6 +290,8 @@ TrayBubbleView::InitParams::InitParams(AnchorType anchor_type, arrow_alignment(BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE) { } +TrayBubbleView::InitParams::InitParams(const InitParams& other) = default; + // static TrayBubbleView* TrayBubbleView::Create(gfx::NativeView parent_window, View* anchor, @@ -322,16 +324,15 @@ TrayBubbleView::TrayBubbleView(gfx::NativeView parent_window, params_(init_params), delegate_(delegate), preferred_width_(init_params.min_width), - bubble_border_(NULL), + bubble_border_(new TrayBubbleBorder(this, GetAnchorView(), init_params)), + owned_bubble_border_(bubble_border_), is_gesture_dragging_(false), mouse_actively_entered_(false) { set_parent_window(parent_window); set_notify_enter_exit_on_child(true); set_close_on_deactivate(init_params.close_on_deactivate); set_margins(gfx::Insets()); - bubble_border_ = new TrayBubbleBorder(this, GetAnchorView(), params_); SetPaintToLayer(true); - SetFillsBoundsOpaquely(true); bubble_content_mask_.reset( new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius())); @@ -409,8 +410,9 @@ bool TrayBubbleView::CanActivate() const { } NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(Widget* widget) { - BubbleFrameView* frame = new BubbleFrameView(margins()); - frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>(bubble_border_)); + BubbleFrameView* frame = static_cast<BubbleFrameView*>( + BubbleDelegateView::CreateNonClientFrameView(widget)); + frame->SetBubbleBorder(std::move(owned_bubble_border_)); return frame; } @@ -504,7 +506,6 @@ void TrayBubbleView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { if (details.is_add && details.child == this) { details.parent->SetPaintToLayer(true); - details.parent->SetFillsBoundsOpaquely(true); details.parent->layer()->SetMasksToBounds(true); } } diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h index db42f4a5107..c0c0c7c2872 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.h +++ b/chromium/ui/views/bubble/tray_bubble_view.h @@ -11,11 +11,6 @@ #include "ui/views/mouse_watcher.h" #include "ui/views/views_export.h" -// Specialized bubble view for bubbles associated with a tray icon (e.g. the -// Ash status area). Mostly this handles custom anchor location and arrow and -// border rendering. This also has its own delegate for handling mouse events -// and other implementation specific details. - namespace ui { class LocatedEvent; } @@ -32,6 +27,10 @@ class TrayBubbleBorder; 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 +// and other implementation specific details. class VIEWS_EXPORT TrayBubbleView : public views::BubbleDelegateView, public views::MouseWatcherListener { public: @@ -96,6 +95,7 @@ class VIEWS_EXPORT TrayBubbleView : public views::BubbleDelegateView, AnchorAlignment anchor_alignment, int min_width, int max_width); + InitParams(const InitParams& other); AnchorType anchor_type; AnchorAlignment anchor_alignment; int min_width; @@ -186,7 +186,10 @@ class VIEWS_EXPORT TrayBubbleView : public views::BubbleDelegateView, InitParams params_; Delegate* delegate_; int preferred_width_; + // |bubble_border_| and |owned_bubble_border_| point to the same thing, but + // the latter ensures we don't leak it before passing off ownership. internal::TrayBubbleBorder* bubble_border_; + scoped_ptr<views::BubbleBorder> owned_bubble_border_; scoped_ptr<internal::TrayBubbleContentMask> bubble_content_mask_; bool is_gesture_dragging_; diff --git a/chromium/ui/views/button_drag_utils.cc b/chromium/ui/views/button_drag_utils.cc index 3fb5d12ed61..60bd6e5abca 100644 --- a/chromium/ui/views/button_drag_utils.cc +++ b/chromium/ui/views/button_drag_utils.cc @@ -14,7 +14,10 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/image/image.h" #include "ui/resources/grit/ui_resources.h" +#include "ui/views/background.h" +#include "ui/views/border.h" #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/resources/grit/views_resources.h" #include "ui/views/widget/widget.h" @@ -49,13 +52,17 @@ void SetDragImage(const GURL& url, button.SetTextSubpixelRenderingEnabled(false); const ui::NativeTheme* theme = widget->GetNativeTheme(); button.SetTextColor(views::Button::STATE_NORMAL, - theme->GetSystemColor(ui::NativeTheme::kColorId_LabelEnabledColor)); - gfx::ShadowValues shadows( - 10, - gfx::ShadowValue(gfx::Vector2d(0, 0), 1.0f, - theme->GetSystemColor( - ui::NativeTheme::kColorId_LabelBackgroundColor))); - button.SetTextShadows(shadows); + theme->GetSystemColor(ui::NativeTheme::kColorId_TextfieldDefaultColor)); + + SkColor bg_color = theme->GetSystemColor( + ui::NativeTheme::kColorId_TextfieldDefaultBackground); + if (widget->IsTranslucentWindowOpacitySupported()) { + button.SetTextShadows(gfx::ShadowValues( + 10, gfx::ShadowValue(gfx::Vector2d(0, 0), 1.0f, bg_color))); + } else { + button.set_background(views::Background::CreateSolidBackground(bg_color)); + button.SetBorder(button.CreateDefaultBorder()); + } button.SetMaxSize(gfx::Size(kLinkDragImageMaxWidth, 0)); if (icon.isNull()) { button.SetImage(views::Button::STATE_NORMAL, diff --git a/chromium/ui/views/cocoa/bridged_content_view.h b/chromium/ui/views/cocoa/bridged_content_view.h index 9f6fbb50566..f4b6a867cd7 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.h +++ b/chromium/ui/views/cocoa/bridged_content_view.h @@ -47,6 +47,9 @@ class View; // Whether dragging on the view moves the window. BOOL mouseDownCanMoveWindow_; + + // The cached window mask. Only used for non-rectangular windows on 10.9. + base::scoped_nsobject<NSBezierPath> windowMask_; } @property(readonly, nonatomic) views::View* hostedView; @@ -73,6 +76,9 @@ class View; // contentRect (also this NSView's frame), as given by a ui::LocatedEvent. - (void)updateTooltipIfRequiredAt:(const gfx::Point&)locationInContent; +// Update windowMask_ depending on the current view bounds. +- (void)updateWindowMask; + @end #endif // UI_VIEWS_COCOA_BRIDGED_CONTENT_VIEW_H_ diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views/cocoa/bridged_content_view.mm index 718207c4013..6d72fef5cf7 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.mm +++ b/chromium/ui/views/cocoa/bridged_content_view.mm @@ -5,9 +5,11 @@ #import "ui/views/cocoa/bridged_content_view.h" #include "base/logging.h" +#import "base/mac/mac_util.h" #import "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" #include "skia/ext/skia_utils_mac.h" +#include "ui/base/cocoa/cocoa_base_utils.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" #include "ui/compositor/canvas_painter.h" @@ -17,6 +19,9 @@ #include "ui/gfx/canvas_paint_mac.h" #include "ui/gfx/geometry/rect.h" #import "ui/gfx/mac/coordinate_conversion.h" +#include "ui/gfx/path.h" +#import "ui/gfx/path_mac.h" +#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_controller.h" @@ -27,6 +32,17 @@ using views::MenuController; namespace { +// Returns true if all four corners of |rect| are contained inside |path|. +bool IsRectInsidePath(NSRect rect, NSBezierPath* path) { + return [path containsPoint:rect.origin] && + [path containsPoint:NSMakePoint(rect.origin.x + rect.size.width, + rect.origin.y)] && + [path containsPoint:NSMakePoint(rect.origin.x, + rect.origin.y + rect.size.height)] && + [path containsPoint:NSMakePoint(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height)]; +} + // Convert a |point| in |source_window|'s AppKit coordinate system (origin at // the bottom left of the window) to |target_window|'s content rect, with the // origin at the top left of the content area. @@ -35,10 +51,11 @@ gfx::Point MovePointToWindow(const NSPoint& point, NSWindow* source_window, NSWindow* target_window) { NSPoint point_in_screen = source_window - ? [source_window convertBaseToScreen:point] + ? ui::ConvertPointFromWindowToScreen(source_window, point) : point; - NSPoint point_in_window = [target_window convertScreenToBase:point_in_screen]; + NSPoint point_in_window = + ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); NSRect content_rect = [target_window contentRectForFrameRect:[target_window frame]]; return gfx::Point(point_in_window.x, @@ -252,6 +269,30 @@ gfx::Rect GetFirstRectForRangeHelper(const ui::TextInputClient* client, } } +- (void)updateWindowMask { + DCHECK(![self inLiveResize]); + DCHECK(base::mac::IsOSMavericksOrEarlier()); + DCHECK(hostedView_); + + views::Widget* widget = hostedView_->GetWidget(); + if (!widget->non_client_view()) + return; + + const NSRect frameRect = [self bounds]; + gfx::Path mask; + widget->non_client_view()->GetWindowMask(gfx::Size(frameRect.size), &mask); + if (mask.isEmpty()) + return; + + windowMask_.reset([gfx::CreateNSBezierPathFromSkPath(mask) retain]); + + // Convert to AppKit coordinate system. + NSAffineTransform* flipTransform = [NSAffineTransform transform]; + [flipTransform translateXBy:0.0 yBy:frameRect.size.height]; + [flipTransform scaleXBy:1.0 yBy:-1.0]; + [windowMask_ transformUsingAffineTransform:flipTransform]; +} + // BridgedContentView private implementation. - (void)handleKeyEvent:(NSEvent*)theEvent { @@ -368,6 +409,20 @@ gfx::Rect GetFirstRectForRangeHelper(const ui::TextInputClient* client, return YES; } +- (BOOL)becomeFirstResponder { + BOOL result = [super becomeFirstResponder]; + if (result && hostedView_) + hostedView_->GetWidget()->GetFocusManager()->RestoreFocusedView(); + return result; +} + +- (BOOL)resignFirstResponder { + BOOL result = [super resignFirstResponder]; + if (result && hostedView_) + hostedView_->GetWidget()->GetFocusManager()->StoreFocusedView(true); + return result; +} + - (void)viewDidMoveToWindow { // When this view is added to a window, AppKit calls setFrameSize before it is // added to the window, so the behavior in setFrameSize is not triggered. @@ -393,6 +448,18 @@ gfx::Rect GetFirstRectForRangeHelper(const ui::TextInputClient* client, hostedView_->SetSize(gfx::Size(newSize.width, newSize.height)); } +- (void)viewDidEndLiveResize { + [super viewDidEndLiveResize]; + + // We prevent updating the window mask and clipping the border around the + // view, during a live resize. Hence update the window mask and redraw the + // view after resize has completed. + if (base::mac::IsOSMavericksOrEarlier()) { + [self updateWindowMask]; + [self setNeedsDisplay:YES]; + } +} + - (void)drawRect:(NSRect)dirtyRect { // Note that BridgedNativeWidget uses -[NSWindow setAutodisplay:NO] to // suppress calls to this when the window is known to be hidden. @@ -407,6 +474,33 @@ gfx::Rect GetFirstRectForRangeHelper(const ui::TextInputClient* client, yRadius:radius] fill]; } + // On OS versions earlier than Yosemite, to generate a drop shadow, we set an + // opaque background. This causes windows with non rectangular shapes to have + // square corners. To get around this, fill the path outside the window + // boundary with clearColor and tell Cococa to regenerate drop shadow. See + // crbug.com/543671. + if (windowMask_ && ![self inLiveResize] && + !IsRectInsidePath(dirtyRect, windowMask_)) { + DCHECK(base::mac::IsOSMavericksOrEarlier()); + gfx::ScopedNSGraphicsContextSaveGState state; + + // The outer rectangular path corresponding to the window. + NSBezierPath* outerPath = [NSBezierPath bezierPathWithRect:[self bounds]]; + + [outerPath appendBezierPath:windowMask_]; + [outerPath setWindingRule:NSEvenOddWindingRule]; + [[NSGraphicsContext currentContext] + setCompositingOperation:NSCompositeCopy]; + [[NSColor clearColor] set]; + + // Fill the region between windowMask_ and its outer rectangular path + // with clear color. This causes the window to have the shape described + // by windowMask_. + [outerPath fill]; + // Regerate drop shadow around the window boundary. + [[self window] invalidateShadow]; + } + // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer(). if (hostedView_->GetWidget()->GetLayer()) return; @@ -416,6 +510,13 @@ gfx::Rect GetFirstRectForRangeHelper(const ui::TextInputClient* client, ui::CanvasPainter(&canvas, 1.f).context()); } +// To maximize consistency with the Cocoa browser (mac_views_browser=0), accept +// mouse clicks immediately so that clicking on Chrome from an inactive window +// will allow the event to be processed, rather than merely activate the window. +- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent { + return YES; +} + - (NSTextInputContext*)inputContext { // If the textInputClient_ does not exist, return nil since this view does not // conform to NSTextInputClient protocol. diff --git a/chromium/ui/views/cocoa/bridged_native_widget.h b/chromium/ui/views/cocoa/bridged_native_widget.h index 8f6523042de..fc521597ee7 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.h +++ b/chromium/ui/views/cocoa/bridged_native_widget.h @@ -49,6 +49,9 @@ class VIEWS_EXPORT BridgedNativeWidget public ui::AcceleratedWidgetMacNSView, public BridgedNativeWidgetOwner { public: + // Contains NativeViewHost->gfx::NativeView associations. + using AssociatedViews = std::map<const views::View*, NSView*>; + // Ways of changing the visibility of the bridged NSWindow. enum WindowVisibilityState { HIDE_WINDOW, // Hides with -[NSWindow orderOut:]. @@ -156,6 +159,12 @@ class VIEWS_EXPORT BridgedNativeWidget // Creates a ui::Compositor which becomes responsible for drawing the window. void CreateLayer(ui::LayerType layer_type, bool translucent); + // Updates |associated_views_| on NativeViewHost::Attach()/Detach(). + void SetAssociationForView(const views::View* view, NSView* native_view); + void ClearAssociationForView(const views::View* view); + // Sorts child NSViews according to NativeViewHosts order in views hierarchy. + void ReorderChildViews(); + NativeWidgetMac* native_widget_mac() { return native_widget_mac_; } BridgedContentView* ns_view() { return bridged_view_; } NSWindow* ns_window() { return window_; } @@ -289,6 +298,8 @@ class VIEWS_EXPORT BridgedNativeWidget // shadow needs to be invalidated when a frame is received for the new shape. bool invalidate_shadow_on_frame_swap_ = false; + AssociatedViews associated_views_; + DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidget); }; diff --git a/chromium/ui/views/cocoa/bridged_native_widget.mm b/chromium/ui/views/cocoa/bridged_native_widget.mm index 24bba74dd91..0ccff9d15a0 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget.mm @@ -83,8 +83,20 @@ CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection, namespace { +using RankMap = std::map<NSView*, int>; + +// SDK 10.11 contains incompatible changes of sortSubviewsUsingFunction. +// It takes (__kindof NSView*) as comparator argument. +// https://llvm.org/bugs/show_bug.cgi?id=25149 +#if !defined(MAC_OS_X_VERSION_10_11) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11 +using NSViewComparatorValue = id; +#else +using NSViewComparatorValue = __kindof NSView*; +#endif + const CGFloat kMavericksMenuOpacity = 251.0 / 255.0; -const CGFloat kYosemiteMenuOpacity = 194.0 / 255.0; +const CGFloat kYosemiteMenuOpacity = 177.0 / 255.0; const int kYosemiteMenuBlur = 80; // Margin at edge and corners of the window that trigger resizing. These match @@ -96,7 +108,7 @@ int kWindowPropertiesKey; float GetDeviceScaleFactorFromView(NSView* view) { gfx::Display display = - gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view); + gfx::Screen::GetScreen()->GetDisplayNearestWindow(view); DCHECK(display.is_valid()); return display.device_scale_factor(); } @@ -263,6 +275,40 @@ scoped_refptr<base::SingleThreadTaskRunner> GetCompositorTaskRunner() { return task_runner ? task_runner : base::ThreadTaskRunnerHandle::Get(); } +void RankNSViews(views::View* view, + const views::BridgedNativeWidget::AssociatedViews& hosts, + RankMap* rank) { + auto it = hosts.find(view); + if (it != hosts.end()) + rank->emplace(it->second, rank->size()); + for (int i = 0; i < view->child_count(); ++i) + RankNSViews(view->child_at(i), hosts, rank); +} + +NSComparisonResult SubviewSorter(NSViewComparatorValue lhs, + NSViewComparatorValue rhs, + void* rank_as_void) { + DCHECK_NE(lhs, rhs); + + const RankMap* rank = static_cast<const RankMap*>(rank_as_void); + auto left_rank = rank->find(lhs); + auto right_rank = rank->find(rhs); + bool left_found = left_rank != rank->end(); + bool right_found = right_rank != rank->end(); + + // Sort unassociated views above associated views. + if (left_found != right_found) + return left_found ? NSOrderedAscending : NSOrderedDescending; + + if (left_found) { + return left_rank->second < right_rank->second ? NSOrderedAscending + : NSOrderedDescending; + } + + // If both are unassociated, consider that order is not important + return NSOrderedSame; +} + } // namespace namespace views { @@ -293,11 +339,7 @@ BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) } BridgedNativeWidget::~BridgedNativeWidget() { - RemoveOrDestroyChildren(); - DCHECK(child_windows_.empty()); - SetFocusManager(NULL); - SetRootView(NULL); - DestroyCompositor(); + bool close_window = false; if ([window_ delegate]) { // If the delegate is still set on a modal dialog, it means it was not // closed via [NSApplication endSheet:]. This is probably OK if the widget @@ -308,12 +350,31 @@ BridgedNativeWidget::~BridgedNativeWidget() { // So ban it. Modal dialogs should be closed via Widget::Close(). DCHECK(!native_widget_mac_->IsWindowModalSheet()); - // If the delegate is still set, it means OnWindowWillClose has not been - // called and the window is still open. Calling -[NSWindow close] will - // synchronously call OnWindowWillClose and notify NativeWidgetMac. + // If the delegate is still set, it means OnWindowWillClose() has not been + // called and the window is still open. Usually, -[NSWindow close] would + // synchronously call OnWindowWillClose() which removes the delegate and + // notifies NativeWidgetMac, which then calls this with a nil delegate. + // For other teardown flows (e.g. Widget::WIDGET_OWNS_NATIVE_WIDGET or + // Widget::CloseNow()) the delegate must first be cleared to avoid AppKit + // calling back into the bridge. This means OnWindowWillClose() needs to be + // invoked manually, which is done below. + // Note that if the window has children it can't be closed until the + // children are gone, but removing child windows calls into AppKit for the + // parent window, so the delegate must be cleared first. + [window_ setDelegate:nil]; + close_window = true; + } + + RemoveOrDestroyChildren(); + DCHECK(child_windows_.empty()); + SetFocusManager(nullptr); + SetRootView(nullptr); + DestroyCompositor(); + + if (close_window) { + OnWindowWillClose(); [window_ close]; } - DCHECK(![window_ delegate]); } void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window, @@ -645,11 +706,6 @@ void BridgedNativeWidget::ToggleDesiredFullscreenState() { if (!window_visible_) SetVisibilityState(SHOW_INACTIVE); - if (base::mac::IsOSSnowLeopard()) { - NOTIMPLEMENTED(); - return; // TODO(tapted): Implement this for Snow Leopard. - } - // Enable fullscreen collection behavior because: // 1: -[NSWindow toggleFullscreen:] would otherwise be ignored, // 2: the fullscreen button must be enabled so the user can leave fullscreen. @@ -667,6 +723,14 @@ void BridgedNativeWidget::OnSizeChanged() { if ([window_ inLiveResize]) MaybeWaitForFrame(new_size); } + + // 10.9 is unable to generate a window shadow from the composited CALayer, so + // use Quartz. + // We don't update the window mask during a live resize, instead it is done + // after the resize is completed in viewDidEndLiveResize: in + // BridgedContentView. + if (base::mac::IsOSMavericksOrEarlier() && ![window_ inLiveResize]) + [bridged_view_ updateWindowMask]; } void BridgedNativeWidget::OnVisibilityChanged() { @@ -850,12 +914,42 @@ void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type, // native shape is what's most appropriate for displaying sheets on Mac. if (translucent && !native_widget_mac_->IsWindowModalSheet()) { [window_ setOpaque:NO]; - [window_ setBackgroundColor:[NSColor clearColor]]; + // For Mac OS versions earlier than Yosemite, the Window server isn't able + // to generate a window shadow from the composited CALayer. To get around + // this, let the window background remain opaque and clip the window + // boundary in drawRect method of BridgedContentView. See crbug.com/543671. + if (base::mac::IsOSYosemiteOrLater()) + [window_ setBackgroundColor:[NSColor clearColor]]; } UpdateLayerProperties(); } +void BridgedNativeWidget::SetAssociationForView(const views::View* view, + NSView* native_view) { + DCHECK_EQ(0u, associated_views_.count(view)); + associated_views_[view] = native_view; + native_widget_mac_->GetWidget()->ReorderNativeViews(); +} + +void BridgedNativeWidget::ClearAssociationForView(const views::View* view) { + auto it = associated_views_.find(view); + DCHECK(it != associated_views_.end()); + associated_views_.erase(it); +} + +void BridgedNativeWidget::ReorderChildViews() { + RankMap rank; + Widget* widget = native_widget_mac_->GetWidget(); + RankNSViews(widget->GetRootView(), associated_views_, &rank); + // Unassociated NSViews should be ordered above associated ones. The exception + // is the UI compositor's superview, which should always be on the very + // bottom, so give it an explicit negative rank. + if (compositor_superview_) + rank[compositor_superview_] = -1; + [bridged_view_ sortSubviewsUsingFunction:&SubviewSorter context:&rank]; +} + //////////////////////////////////////////////////////////////////////////////// // BridgedNativeWidget, internal::InputMethodDelegate: @@ -918,7 +1012,6 @@ void BridgedNativeWidget::OnDeviceScaleFactorChanged( } base::Closure BridgedNativeWidget::PrepareForLayerBoundsChange() { - NOTIMPLEMENTED(); return base::Closure(); } @@ -1053,11 +1146,7 @@ void BridgedNativeWidget::CreateCompositor() { AddCompositorSuperview(); - // TODO(tapted): Get this value from GpuDataManagerImpl via ViewsDelegate. - bool needs_gl_finish_workaround = false; - - compositor_widget_.reset( - new ui::AcceleratedWidgetMac(needs_gl_finish_workaround)); + compositor_widget_.reset(new ui::AcceleratedWidgetMac()); compositor_.reset( new ui::Compositor(context_factory, GetCompositorTaskRunner())); compositor_->SetAcceleratedWidget(compositor_widget_->accelerated_widget()); 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 5e8579f9034..79fdac10510 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm @@ -11,17 +11,40 @@ #include "base/macros.h" #include "ui/base/hit_test.h" #import "ui/base/test/nswindow_fullscreen_notification_waiter.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/test/widget_test.h" +#include "ui/views/widget/native_widget_mac.h" #include "ui/views/window/native_frame_view.h" namespace views { namespace test { +namespace { + +// Provide a resizable Widget by default. Starting in 10.11, OSX doesn't +// correctly restore the window size when coming out of fullscreen if the window +// is not user-sizable. +class ResizableDelegateView : public WidgetDelegateView { + public: + ResizableDelegateView() {} + + // WidgetDelgate: + bool CanResize() const override { return true; } + + private: + DISALLOW_COPY_AND_ASSIGN(ResizableDelegateView); +}; + +} // namespace class BridgedNativeWidgetUITest : public test::WidgetTest { public: - BridgedNativeWidgetUITest() {} + BridgedNativeWidgetUITest() { + // TODO(tapted): Remove this when these are absorbed into Chrome's + // interactive_ui_tests target. See http://crbug.com/403679. + ui_controls::EnableUIControls(); + } // testing::Test: void SetUp() override { @@ -29,6 +52,8 @@ class BridgedNativeWidgetUITest : public test::WidgetTest { Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW); init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.bounds = gfx::Rect(100, 100, 300, 200); + init_params.delegate = new ResizableDelegateView; widget_.reset(new Widget); widget_->Init(init_params); } @@ -52,8 +77,6 @@ class BridgedNativeWidgetUITest : public test::WidgetTest { // by the Widget code or elsewhere (e.g. by the user). TEST_F(BridgedNativeWidgetUITest, FullscreenSynchronousState) { EXPECT_FALSE(widget_->IsFullscreen()); - if (base::mac::IsOSSnowLeopard()) - return; // Allow user-initiated fullscreen changes on the Window. [test_window() @@ -121,11 +144,6 @@ TEST_F(BridgedNativeWidgetUITest, FullscreenEnterAndExit) { EXPECT_FALSE(widget_->IsVisible()); widget_->SetFullscreen(true); EXPECT_TRUE(widget_->IsVisible()); - if (base::mac::IsOSSnowLeopard()) { - // On Snow Leopard, SetFullscreen() isn't implemented. But shouldn't crash. - EXPECT_FALSE(widget_->IsFullscreen()); - return; - } EXPECT_TRUE(widget_->IsFullscreen()); EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds()); @@ -152,9 +170,6 @@ TEST_F(BridgedNativeWidgetUITest, FullscreenEnterAndExit) { // Test that Widget::Restore exits fullscreen. TEST_F(BridgedNativeWidgetUITest, FullscreenRestore) { - if (base::mac::IsOSSnowLeopard()) - return; - base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter( [[NSWindowFullscreenNotificationWaiter alloc] initWithWindow:test_window()]); diff --git a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm index d289af52176..3b3eb082631 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm @@ -214,8 +214,7 @@ class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { protected: scoped_ptr<views::View> view_; - scoped_ptr<BridgedNativeWidget> bridge_; - BridgedContentView* ns_view_; // Weak. Owned by bridge_. + BridgedContentView* ns_view_; // Weak. Owned by bridge(). base::MessageLoopForUI message_loop_; private: @@ -280,6 +279,8 @@ void BridgedNativeWidgetTest::SetUp() { } void BridgedNativeWidgetTest::TearDown() { + if (bridge()) + bridge()->SetRootView(nullptr); view_.reset(); BridgedNativeWidgetTestBase::TearDown(); } @@ -430,22 +431,22 @@ TEST_F(BridgedNativeWidgetTest, InputContext) { // Test getting complete string using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_GetCompleteString) { - const std::string kTestString = "foo bar baz"; - InstallTextField(kTestString); + const std::string test_string = "foo bar baz"; + InstallTextField(test_string); - NSRange range = NSMakeRange(0, kTestString.size()); + NSRange range = NSMakeRange(0, test_string.size()); NSRange actual_range; NSAttributedString* text = [ns_view_ attributedSubstringForProposedRange:range actualRange:&actual_range]; - EXPECT_EQ(kTestString, SysNSStringToUTF8([text string])); + EXPECT_EQ(test_string, SysNSStringToUTF8([text string])); EXPECT_EQ_RANGE(range, actual_range); } // Test getting middle substring using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_GetMiddleSubstring) { - const std::string kTestString = "foo bar baz"; - InstallTextField(kTestString); + const std::string test_string = "foo bar baz"; + InstallTextField(test_string); NSRange range = NSMakeRange(4, 3); NSRange actual_range; @@ -458,8 +459,8 @@ TEST_F(BridgedNativeWidgetTest, TextInput_GetMiddleSubstring) { // Test getting ending substring using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_GetEndingSubstring) { - const std::string kTestString = "foo bar baz"; - InstallTextField(kTestString); + const std::string test_string = "foo bar baz"; + InstallTextField(test_string); NSRange range = NSMakeRange(8, 100); NSRange actual_range; @@ -473,8 +474,8 @@ TEST_F(BridgedNativeWidgetTest, TextInput_GetEndingSubstring) { // Test getting empty substring using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_GetEmptySubstring) { - const std::string kTestString = "foo bar baz"; - InstallTextField(kTestString); + const std::string test_string = "foo bar baz"; + InstallTextField(test_string); NSRange range = EmptyRange(); NSRange actual_range; @@ -487,21 +488,21 @@ TEST_F(BridgedNativeWidgetTest, TextInput_GetEmptySubstring) { // Test inserting text using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_InsertText) { - const std::string kTestString = "foo"; - InstallTextField(kTestString); + const std::string test_string = "foo"; + InstallTextField(test_string); - [ns_view_ insertText:SysUTF8ToNSString(kTestString) + [ns_view_ insertText:SysUTF8ToNSString(test_string) replacementRange:EmptyRange()]; - gfx::Range range(0, kTestString.size()); + gfx::Range range(0, test_string.size()); base::string16 text; EXPECT_TRUE([ns_view_ textInputClient]->GetTextFromRange(range, &text)); - EXPECT_EQ(ASCIIToUTF16(kTestString), text); + EXPECT_EQ(ASCIIToUTF16(test_string), text); } // Test replacing text using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_ReplaceText) { - const std::string kTestString = "foo bar"; - InstallTextField(kTestString); + const std::string test_string = "foo bar"; + InstallTextField(test_string); [ns_view_ insertText:@"baz" replacementRange:NSMakeRange(4, 3)]; EXPECT_EQ("foo baz", GetText()); @@ -509,8 +510,8 @@ TEST_F(BridgedNativeWidgetTest, TextInput_ReplaceText) { // Test IME composition using text input protocol. TEST_F(BridgedNativeWidgetTest, TextInput_Compose) { - const std::string kTestString = "foo "; - InstallTextField(kTestString); + const std::string test_string = "foo "; + InstallTextField(test_string); EXPECT_FALSE([ns_view_ hasMarkedText]); EXPECT_EQ_RANGE(EmptyRange(), [ns_view_ markedRange]); @@ -522,9 +523,9 @@ TEST_F(BridgedNativeWidgetTest, TextInput_Compose) { selectedRange:NSMakeRange(0, 2) replacementRange:EmptyRange()]; EXPECT_TRUE([ns_view_ hasMarkedText]); - EXPECT_EQ_RANGE(NSMakeRange(kTestString.size(), compositionLength), + EXPECT_EQ_RANGE(NSMakeRange(test_string.size(), compositionLength), [ns_view_ markedRange]); - EXPECT_EQ_RANGE(NSMakeRange(kTestString.size(), 2), [ns_view_ selectedRange]); + EXPECT_EQ_RANGE(NSMakeRange(test_string.size(), 2), [ns_view_ selectedRange]); // Confirm composition. [ns_view_ unmarkText]; @@ -653,16 +654,16 @@ TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) { InstallTextField(""); ui::TextInputClient* client = [ns_view_ textInputClient]; - const base::string16 kTestString = base::ASCIIToUTF16("test_str"); + const base::string16 test_string = base::ASCIIToUTF16("test_str"); const size_t kTextLength = 8; - SetCompositionText(client, kTestString, 1, nullptr); + SetCompositionText(client, test_string, 1, nullptr); // Query bounds for the whole composition string. NSRange query_range = NSMakeRange(0, kTextLength); NSRange actual_range; NSRect rect = [ns_view_ firstRectForCharacterRange:query_range actualRange:&actual_range]; - EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range), + EXPECT_EQ(GetExpectedBoundsForRange(client, test_string, query_range), gfx::ScreenRectFromNSRect(rect)); EXPECT_EQ_RANGE(query_range, actual_range); @@ -670,7 +671,7 @@ TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) { query_range = NSMakeRange(1, 4); rect = [ns_view_ firstRectForCharacterRange:query_range actualRange:&actual_range]; - EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range), + EXPECT_EQ(GetExpectedBoundsForRange(client, test_string, query_range), gfx::ScreenRectFromNSRect(rect)); EXPECT_EQ_RANGE(query_range, actual_range); } @@ -683,9 +684,6 @@ typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest; // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause // the fullscreen transition to fail. TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { - if (base::mac::IsOSSnowLeopard()) - return; - base::scoped_nsobject<NSWindow> owned_window( [[BridgedNativeWidgetTestFullScreenWindow alloc] initWithContentRect:NSMakeRect(50, 50, 400, 300) diff --git a/chromium/ui/views/cocoa/cocoa_mouse_capture.h b/chromium/ui/views/cocoa/cocoa_mouse_capture.h index 0adf134295f..55f87ede81c 100644 --- a/chromium/ui/views/cocoa/cocoa_mouse_capture.h +++ b/chromium/ui/views/cocoa/cocoa_mouse_capture.h @@ -24,7 +24,7 @@ class VIEWS_EXPORT CocoaMouseCapture { ~CocoaMouseCapture(); // True if the event tap is active (i.e. not stolen by a later instance). - bool IsActive() const { return active_handle_; } + bool IsActive() const { return !!active_handle_; } private: class ActiveEventTap; diff --git a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm index 678e4d58440..4acbd946877 100644 --- a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm +++ b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm @@ -5,6 +5,7 @@ #import "ui/views/cocoa/native_widget_mac_nswindow.h" #include "base/mac/foundation_util.h" +#import "ui/views/cocoa/bridged_native_widget.h" #import "ui/base/cocoa/user_interface_item_command_handler.h" #import "ui/views/cocoa/views_nswindow_delegate.h" #include "ui/views/controls/menu/menu_controller.h" @@ -83,9 +84,17 @@ if (![self delegate]) return NO; - // Dialogs shouldn't take large shadows away from their parent window. + // Dialogs and bubbles shouldn't take large shadows away from their parent. views::Widget* widget = [self viewsWidget]; - return widget->CanActivate() && !widget->IsDialogBox(); + return widget->CanActivate() && + !views::NativeWidgetMac::GetBridgeForNativeWindow(self)->parent(); +} + +// Lets the traffic light buttons on the parent window keep their active state. +- (BOOL)_sharesParentKeyState { + // Follow -canBecomeMainWindow unless the window provides its own buttons. + return ([self styleMask] & NSClosableWindowMask) == 0 && + ![self canBecomeMainWindow]; } // Override sendEvent to allow key events to be forwarded to a toolkit-views diff --git a/chromium/ui/views/cocoa/tooltip_manager_mac.h b/chromium/ui/views/cocoa/tooltip_manager_mac.h index a7977a9aa2f..208fb4e948a 100644 --- a/chromium/ui/views/cocoa/tooltip_manager_mac.h +++ b/chromium/ui/views/cocoa/tooltip_manager_mac.h @@ -18,8 +18,7 @@ class TooltipManagerMac : public TooltipManager { ~TooltipManagerMac() override; // TooltipManager: - int GetMaxWidth(const gfx::Point& location, - gfx::NativeView context) const override; + int GetMaxWidth(const gfx::Point& location) const override; const gfx::FontList& GetFontList() const override; void UpdateTooltip() override; void TooltipTextChanged(View* view) override; diff --git a/chromium/ui/views/cocoa/tooltip_manager_mac.mm b/chromium/ui/views/cocoa/tooltip_manager_mac.mm index b96d1227715..99a1fb5d906 100644 --- a/chromium/ui/views/cocoa/tooltip_manager_mac.mm +++ b/chromium/ui/views/cocoa/tooltip_manager_mac.mm @@ -4,6 +4,7 @@ #include "ui/views/cocoa/tooltip_manager_mac.h" +#include "ui/base/cocoa/cocoa_base_utils.h" #include "ui/gfx/font_list.h" #import "ui/gfx/mac/coordinate_conversion.h" #import "ui/views/cocoa/bridged_content_view.h" @@ -25,8 +26,7 @@ TooltipManagerMac::TooltipManagerMac(BridgedNativeWidget* widget) TooltipManagerMac::~TooltipManagerMac() { } -int TooltipManagerMac::GetMaxWidth(const gfx::Point& location, - gfx::NativeView context) const { +int TooltipManagerMac::GetMaxWidth(const gfx::Point& location) const { return kTooltipMaxWidthPixels; } @@ -40,7 +40,8 @@ void TooltipManagerMac::UpdateTooltip() { NSWindow* window = widget_->ns_window(); BridgedContentView* view = widget_->ns_view(); - NSPoint nspoint = [window convertScreenToBase:[NSEvent mouseLocation]]; + NSPoint nspoint = + ui::ConvertPointFromScreenToWindow(window, [NSEvent mouseLocation]); // Note: flip in the view's frame, which matches the window's contentRect. gfx::Point point(nspoint.x, NSHeight([view frame]) - nspoint.y); [view updateTooltipIfRequiredAt:point]; diff --git a/chromium/ui/views/cocoa/views_scrollbar_bridge.h b/chromium/ui/views/cocoa/views_scrollbar_bridge.h new file mode 100644 index 00000000000..eb070a161c5 --- /dev/null +++ b/chromium/ui/views/cocoa/views_scrollbar_bridge.h @@ -0,0 +1,40 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_COCOA_VIEWS_SCROLLBAR_BRIDGE_DELEGATE_H_ +#define UI_VIEWS_COCOA_VIEWS_SCROLLBAR_BRIDGE_DELEGATE_H_ + +#import <Cocoa/Cocoa.h> + +#import "base/mac/scoped_nsobject.h" +#include "ui/views/views_export.h" + +// The delegate set to ViewsScrollbarBridge. +class ViewsScrollbarBridgeDelegate { + public: + // Invoked by ViewsScrollbarBridge when the system informs the process that + // the preferred scroller style has changed + virtual void OnScrollerStyleChanged() = 0; +}; + +// A bridge to NSScroller managed by NativeCocoaScrollbar. Serves as a helper +// class to bridge NSScroller notifications and functions to CocoaScrollbar. +@interface ViewsScrollbarBridge : NSObject { + @private + ViewsScrollbarBridgeDelegate* delegate_; // Weak. Owns this. +} + +// Initializes with the given delegate and registers for notifications on +// scroller style changes. +- (id)initWithDelegate:(ViewsScrollbarBridgeDelegate*)delegate; + +// Sets |delegate_| to nullptr. +-(void)clearDelegate; + +// Returns the style of scrollers that OSX is using. ++ (NSScrollerStyle)getPreferredScrollerStyle; + +@end + +#endif
\ No newline at end of file diff --git a/chromium/ui/views/cocoa/views_scrollbar_bridge.mm b/chromium/ui/views/cocoa/views_scrollbar_bridge.mm new file mode 100644 index 00000000000..d80dece0e7b --- /dev/null +++ b/chromium/ui/views/cocoa/views_scrollbar_bridge.mm @@ -0,0 +1,49 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ui/views/cocoa/views_scrollbar_bridge.h" + +#import "base/mac/sdk_forward_declarations.h" + +@interface ViewsScrollbarBridge () + +// Called when we receive a NSPreferredScrollerStyleDidChangeNotification. +- (void)onScrollerStyleChanged:(NSNotification*)notification; + +@end + +@implementation ViewsScrollbarBridge + +- (id)initWithDelegate:(ViewsScrollbarBridgeDelegate*)delegate { + if ((self = [super init])) { + delegate_ = delegate; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(onScrollerStyleChanged:) + name:NSPreferredScrollerStyleDidChangeNotification + object:nil]; + } + return self; +} + +- (void)dealloc { + DCHECK(!delegate_); + [super dealloc]; +} + +- (void)clearDelegate { + delegate_ = nullptr; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)onScrollerStyleChanged:(NSNotification*)notification { + if (delegate_) + delegate_->OnScrollerStyleChanged(); +} + ++ (NSScrollerStyle)getPreferredScrollerStyle { + return [NSScroller preferredScrollerStyle]; +} + +@end diff --git a/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.mm b/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.mm index 0fbe5d545c2..d85184ef5a4 100644 --- a/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.mm +++ b/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.mm @@ -7,6 +7,7 @@ #import <Cocoa/Cocoa.h> #include "base/logging.h" +#include "base/mac/sdk_forward_declarations.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/vector2d.h" #import "ui/gfx/mac/coordinate_conversion.h" @@ -72,14 +73,12 @@ NSWindow* WidgetOwnerNSWindowAdapter::GetNSWindow() { gfx::Vector2d WidgetOwnerNSWindowAdapter::GetChildWindowOffset() const { NSRect rect_in_window = [anchor_view_ convertRect:[anchor_view_ bounds] toView:nil]; - // Ensure we anchor off the top-left of |anchor_view_| (rect_in_window.origin + NSRect rect_in_screen = [anchor_window_ convertRectToScreen:rect_in_window]; + // Ensure we anchor off the top-left of |anchor_view_| (rect_in_screen.origin // is the bottom-left of the view). - // TODO(tapted): Use -[NSWindow convertRectToScreen:] when we ditch 10.6. - NSRect rect_in_screen = NSZeroRect; - rect_in_screen.origin = - [anchor_window_ convertBaseToScreen:NSMakePoint(NSMinX(rect_in_window), - NSMaxY(rect_in_window))]; - return gfx::ScreenRectFromNSRect(rect_in_screen).OffsetFromOrigin(); + NSPoint anchor_in_screen = + NSMakePoint(NSMinX(rect_in_screen), NSMaxY(rect_in_screen)); + return gfx::ScreenPointFromNSPoint(anchor_in_screen).OffsetFromOrigin(); } bool WidgetOwnerNSWindowAdapter::IsVisibleParent() const { diff --git a/chromium/ui/views/color_chooser/color_chooser_view.cc b/chromium/ui/views/color_chooser/color_chooser_view.cc index 2c63883c617..e0aea825e9e 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.cc +++ b/chromium/ui/views/color_chooser/color_chooser_view.cc @@ -103,11 +103,9 @@ void DrawGradientRect(const gfx::Rect& rect, SkColor start_color, points[1].iset(rect.width() + 1, 0); else points[1].iset(0, rect.height() + 1); - skia::RefPtr<SkShader> shader(skia::AdoptRef( - SkGradientShader::CreateLinear(points, colors, NULL, 2, - SkShader::kClamp_TileMode))); SkPaint paint; - paint.setShader(shader.get()); + paint.setShader(SkGradientShader::MakeLinear(points, colors, NULL, 2, + SkShader::kClamp_TileMode)); canvas->DrawRect(rect, paint); } diff --git a/chromium/ui/views/controls/button/blue_button.cc b/chromium/ui/views/controls/button/blue_button.cc index 3740bb11a36..12ea638c160 100644 --- a/chromium/ui/views/controls/button/blue_button.cc +++ b/chromium/ui/views/controls/button/blue_button.cc @@ -53,7 +53,7 @@ const char* BlueButton::GetClassName() const { scoped_ptr<LabelButtonBorder> BlueButton::CreateDefaultBorder() const { // Insets for splitting the images. - const gfx::Insets insets(5, 5, 5, 5); + const gfx::Insets insets(5); scoped_ptr<LabelButtonAssetBorder> button_border( new LabelButtonAssetBorder(style())); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h index 0f1160b11f1..dab4870ad53 100644 --- a/chromium/ui/views/controls/button/button.h +++ b/chromium/ui/views/controls/button/button.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "ui/native_theme/native_theme.h" -#include "ui/views/view.h" +#include "ui/views/animation/ink_drop_host_view.h" namespace views { @@ -26,7 +26,7 @@ class VIEWS_EXPORT 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 View { +class VIEWS_EXPORT Button : public InkDropHostView { public: ~Button() override; diff --git a/chromium/ui/views/controls/button/custom_button.cc b/chromium/ui/views/controls/button/custom_button.cc index 86fca746d59..6e80eb12d9f 100644 --- a/chromium/ui/views/controls/button/custom_button.cc +++ b/chromium/ui/views/controls/button/custom_button.cc @@ -5,12 +5,17 @@ #include "ui/views/controls/button/custom_button.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/base/material_design/material_design_controller.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/canvas.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/screen.h" +#include "ui/native_theme/native_theme.h" #include "ui/views/animation/ink_drop_delegate.h" +#include "ui/views/animation/ink_drop_hover.h" #include "ui/views/controls/button/blue_button.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" @@ -26,20 +31,51 @@ namespace views { +namespace { + // How long the hover animation takes if uninterrupted. -static const int kHoverFadeDurationMs = 150; +const int kHoverFadeDurationMs = 150; -// static -const char CustomButton::kViewClassName[] = "CustomButton"; +// The amount to enlarge the focus border in all directions relative to the +// button. +const int kFocusBorderOutset = -2; + +// The corner radius of the focus border roundrect. +const int kFocusBorderCornerRadius = 3; + +class MdFocusRing : public views::View { + public: + MdFocusRing() { + SetPaintToLayer(true); + layer()->SetFillsBoundsOpaquely(false); + + // Don't accept input events. + SetEnabled(false); + } + ~MdFocusRing() override {} + + void OnPaint(gfx::Canvas* canvas) override { + CustomButton::PaintMdFocusRing(canvas, this); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MdFocusRing); +}; + +} // namespace //////////////////////////////////////////////////////////////////////////////// // CustomButton, public: // static +const char CustomButton::kViewClassName[] = "CustomButton"; + +// static const CustomButton* CustomButton::AsCustomButton(const views::View* view) { - return AsCustomButton(const_cast<views::View*>(view)); + return AsCustomButton(const_cast<View*>(view)); } +// static CustomButton* CustomButton::AsCustomButton(views::View* view) { if (view) { const char* classname = view->GetClassName(); @@ -55,6 +91,19 @@ CustomButton* CustomButton::AsCustomButton(views::View* view) { return NULL; } +// static +void CustomButton::PaintMdFocusRing(gfx::Canvas* canvas, views::View* view) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(view->GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_CallToActionColor)); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(1); + gfx::RectF rect(view->GetLocalBounds()); + rect.Inset(gfx::InsetsF(0.5)); + canvas->DrawRoundRect(rect, kFocusBorderCornerRadius, paint); +} + CustomButton::~CustomButton() {} void CustomButton::SetState(ButtonState state) { @@ -106,7 +155,7 @@ void CustomButton::SetHotTracked(bool is_hot_tracked) { SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL); if (is_hot_tracked) - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); + NotifyAccessibilityEvent(ui::AX_EVENT_HOVER, true); } bool CustomButton::IsHotTracked() const { @@ -116,13 +165,9 @@ bool CustomButton::IsHotTracked() const { //////////////////////////////////////////////////////////////////////////////// // CustomButton, View overrides: -void CustomButton::Layout() { - Button::Layout(); - if (ink_drop_delegate_) - ink_drop_delegate_->OnLayout(); -} - 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; @@ -130,6 +175,9 @@ void CustomButton::OnEnabledChanged() { SetState(ShouldEnterHoveredState() ? STATE_HOVERED : STATE_NORMAL); else SetState(STATE_DISABLED); + + if (ink_drop_delegate_) + ink_drop_delegate_->SetHovered(ShouldShowInkDropHover()); } const char* CustomButton::GetClassName() const { @@ -139,7 +187,8 @@ const char* CustomButton::GetClassName() const { bool CustomButton::OnMousePressed(const ui::MouseEvent& event) { if (state_ == STATE_DISABLED) return true; - if (ShouldEnterPushedState(event) && HitTestPoint(event.location())) { + if (state_ != STATE_PRESSED && ShouldEnterPushedState(event) && + HitTestPoint(event.location())) { SetState(STATE_PRESSED); if (ink_drop_delegate_) ink_drop_delegate_->OnAction(views::InkDropState::ACTION_PENDING); @@ -183,8 +232,12 @@ void CustomButton::OnMouseReleased(const ui::MouseEvent& event) { } void CustomButton::OnMouseCaptureLost() { - // Starting a drag results in a MouseCaptureLost, we need to ignore it. - if (state_ != STATE_DISABLED && !InDrag()) + // Starting a drag results in a MouseCaptureLost. Reset button state. + // TODO(varkha) While in drag only reset the state with Material Design. + // The same logic may applies everywhere so gather any feedback and update. + bool reset_button_state = + !InDrag() || ui::MaterialDesignController::IsModeMaterial(); + if (state_ != STATE_DISABLED && reset_button_state) SetState(STATE_NORMAL); if (ink_drop_delegate_) ink_drop_delegate_->OnAction(views::InkDropState::HIDDEN); @@ -272,6 +325,13 @@ bool CustomButton::AcceleratorPressed(const ui::Accelerator& accelerator) { 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. + return (event.key_code() == ui::VKEY_SPACE) || + (event.key_code() == ui::VKEY_RETURN); +} + void CustomButton::ShowContextMenu(const gfx::Point& p, ui::MenuSourceType source_type) { if (!context_menu_controller()) @@ -281,6 +341,10 @@ void CustomButton::ShowContextMenu(const gfx::Point& p, // 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_ && ink_drop_delegate_) { + ink_drop_delegate_->SetHovered(false); + ink_drop_delegate_->OnAction(InkDropState::HIDDEN); + } View::ShowContextMenu(p, source_type); } @@ -318,6 +382,14 @@ void CustomButton::VisibilityChanged(View* starting_from, bool visible) { SetState(visible && ShouldEnterHoveredState() ? STATE_HOVERED : STATE_NORMAL); } +scoped_ptr<InkDropHover> CustomButton::CreateInkDropHover() const { + return ShouldShowInkDropHover() ? Button::CreateInkDropHover() : nullptr; +} + +SkColor CustomButton::GetInkDropBaseColor() const { + return ink_drop_base_color_; +} + //////////////////////////////////////////////////////////////////////////////// // CustomButton, gfx::AnimationDelegate implementation: @@ -326,23 +398,34 @@ void CustomButton::AnimationProgressed(const gfx::Animation* animation) { } //////////////////////////////////////////////////////////////////////////////// -// CustomButton, views::InkDropHost implementation: +// CustomButton, View overrides (public): -void CustomButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { - SetPaintToLayer(true); - SetFillsBoundsOpaquely(false); - layer()->Add(ink_drop_layer); - layer()->StackAtBottom(ink_drop_layer); +void CustomButton::Layout() { + Button::Layout(); + gfx::Rect focus_bounds = GetLocalBounds(); + focus_bounds.Inset(gfx::Insets(kFocusBorderOutset)); + if (md_focus_ring_) + md_focus_ring_->SetBoundsRect(focus_bounds); } -void CustomButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { - layer()->Remove(ink_drop_layer); - SetFillsBoundsOpaquely(true); - SetPaintToLayer(false); +void CustomButton::ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) { + if (!details.is_add && state_ != STATE_DISABLED) + SetState(STATE_NORMAL); } -gfx::Point CustomButton::CalculateInkDropCenter() const { - return GetLocalBounds().CenterPoint(); +void CustomButton::OnFocus() { + Button::OnFocus(); + if (md_focus_ring_) + md_focus_ring_->SetVisible(true); +} + +void CustomButton::OnBlur() { + Button::OnBlur(); + if (IsHotTracked()) + SetState(STATE_NORMAL); + if (md_focus_ring_) + md_focus_ring_->SetVisible(false); } //////////////////////////////////////////////////////////////////////////////// @@ -359,7 +442,10 @@ CustomButton::CustomButton(ButtonListener* listener) ink_drop_delegate_(nullptr), notify_action_(NOTIFY_ON_RELEASE), has_ink_drop_action_on_click_(false), - ink_drop_action_on_click_(InkDropState::QUICK_ACTION) { + ink_drop_action_on_click_(InkDropState::ACTION_TRIGGERED), + hide_ink_drop_when_showing_context_menu_(true), + ink_drop_base_color_(gfx::kPlaceholderColor), + md_focus_ring_(nullptr) { hover_animation_.SetSlideDuration(kHoverFadeDurationMs); } @@ -377,6 +463,10 @@ bool CustomButton::ShouldEnterPushedState(const ui::Event& event) { return IsTriggerableEvent(event); } +bool CustomButton::ShouldShowInkDropHover() const { + return enabled() && IsMouseHovered() && !InDrag(); +} + bool CustomButton::ShouldEnterHoveredState() { if (!visible()) return false; @@ -401,25 +491,16 @@ bool CustomButton::ShouldEnterHoveredState() { return check_mouse_position && IsMouseHovered(); } -//////////////////////////////////////////////////////////////////////////////// -// CustomButton, View overrides (protected): - -void CustomButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { - Button::OnBoundsChanged(previous_bounds); - if (ink_drop_delegate_) - ink_drop_delegate_->OnLayout(); +void CustomButton::UseMdFocusRing() { + DCHECK(!md_focus_ring_); + md_focus_ring_ = new MdFocusRing(); + AddChildView(md_focus_ring_); + md_focus_ring_->SetVisible(false); + set_request_focus_on_press(false); } -void CustomButton::ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) { - if (!details.is_add && state_ != STATE_DISABLED) - SetState(STATE_NORMAL); -} - -void CustomButton::OnBlur() { - if (IsHotTracked()) - SetState(STATE_NORMAL); -} +//////////////////////////////////////////////////////////////////////////////// +// CustomButton, Button overrides (protected): void CustomButton::NotifyClick(const ui::Event& event) { if (ink_drop_delegate() && has_ink_drop_action_on_click_) diff --git a/chromium/ui/views/controls/button/custom_button.h b/chromium/ui/views/controls/button/custom_button.h index d92f0f04078..cd94e3c3095 100644 --- a/chromium/ui/views/controls/button/custom_button.h +++ b/chromium/ui/views/controls/button/custom_button.h @@ -10,7 +10,6 @@ #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_host.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/controls/button/button.h" @@ -22,9 +21,7 @@ class InkDropDelegate; // Note that this type of button is not focusable by default and will not be // part of the focus chain. Call SetFocusable(true) to make it part of the // focus chain. -class VIEWS_EXPORT CustomButton : public Button, - public gfx::AnimationDelegate, - public views::InkDropHost { +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 { @@ -35,8 +32,11 @@ class VIEWS_EXPORT CustomButton : public Button, // The menu button's class name. static const char kViewClassName[]; - static const CustomButton* AsCustomButton(const views::View* view); - static CustomButton* AsCustomButton(views::View* view); + static const CustomButton* AsCustomButton(const View* view); + static CustomButton* AsCustomButton(View* view); + + // Paint an MD-style focus ring on the given canvas at the given bounds. + static void PaintMdFocusRing(gfx::Canvas* canvas, View* view); ~CustomButton() override; @@ -75,11 +75,18 @@ class VIEWS_EXPORT CustomButton : public Button, 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 SetHotTracked(bool is_hot_tracked); bool IsHotTracked() const; // Overridden from View: - void Layout() override; void OnEnabledChanged() override; const char* GetClassName() const override; bool OnMousePressed(const ui::MouseEvent& event) override; @@ -93,19 +100,26 @@ class VIEWS_EXPORT CustomButton : public Button, 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; void GetAccessibleState(ui::AXViewState* state) override; void VisibilityChanged(View* starting_from, bool is_visible) override; + scoped_ptr<InkDropHover> CreateInkDropHover() const override; + SkColor GetInkDropBaseColor() const override; // Overridden from gfx::AnimationDelegate: void AnimationProgressed(const gfx::Animation* animation) override; - // Overridden from views::InkDropHost: - void AddInkDropLayer(ui::Layer* ink_drop_layer) override; - void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; - gfx::Point CalculateInkDropCenter() const override; + InkDropDelegate* ink_drop_delegate() const { return ink_drop_delegate_; } + + // Overridden from View: + void Layout() override; + 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. @@ -126,6 +140,9 @@ class VIEWS_EXPORT CustomButton : public Button, // we simply return IsTriggerableEvent(event). virtual bool ShouldEnterPushedState(const ui::Event& event); + // Returns true if hover effect should be visible. + virtual bool ShouldShowInkDropHover() const; + 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; } @@ -136,16 +153,12 @@ class VIEWS_EXPORT CustomButton : public Button, // state). This does not take into account enabled state. bool ShouldEnterHoveredState(); - InkDropDelegate* ink_drop_delegate() const { return ink_drop_delegate_; } void set_ink_drop_delegate(InkDropDelegate* ink_drop_delegate) { ink_drop_delegate_ = ink_drop_delegate; } - // Overridden from View: - void OnBoundsChanged(const gfx::Rect& previous_bounds) override; - void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) override; - void OnBlur() override; + // When called, creates and uses |md_focus_ring_| instead of a focus painter. + void UseMdFocusRing(); // Overridden from Button: void NotifyClick(const ui::Event& event) override; @@ -189,6 +202,18 @@ class VIEWS_EXPORT CustomButton : public Button, // is clicked. InkDropState 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_; + + // The MD-style focus ring. This is not done via a FocusPainter + // because it needs to paint to a layer so it can extend beyond the bounds of + // |this|. + views::View* md_focus_ring_; + DISALLOW_COPY_AND_ASSIGN(CustomButton); }; diff --git a/chromium/ui/views/controls/button/custom_button_unittest.cc b/chromium/ui/views/controls/button/custom_button_unittest.cc index 134f7f8db61..bd34cb036c4 100644 --- a/chromium/ui/views/controls/button/custom_button_unittest.cc +++ b/chromium/ui/views/controls/button/custom_button_unittest.cc @@ -8,11 +8,15 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/layout.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/screen.h" #include "ui/views/animation/ink_drop_delegate.h" #include "ui/views/animation/ink_drop_host.h" +#include "ui/views/animation/test/test_ink_drop_delegate.h" +#include "ui/views/animation/test/test_ink_drop_host.h" +#include "ui/views/context_menu_controller.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/label_button.h" @@ -30,8 +34,25 @@ namespace views { +using test::TestInkDropDelegate; + namespace { +// No-op test double of a ContextMenuController. +class TestContextMenuController : public ContextMenuController { + public: + TestContextMenuController() {} + ~TestContextMenuController() override {} + + // ContextMenuController: + void ShowContextMenuForView(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestContextMenuController); +}; + class TestCustomButton : public CustomButton, public ButtonListener { public: explicit TestCustomButton() @@ -64,71 +85,58 @@ class TestCustomButton : public CustomButton, public ButtonListener { }; // An InkDropDelegate that keeps track of ink drop visibility. -class TestInkDropDelegate : public InkDropDelegate { +class TestInkDropDelegateThatTracksVisibilty : public InkDropDelegate { public: - TestInkDropDelegate(InkDropHost* ink_drop_host, - bool* ink_shown, - bool* ink_hidden) - : ink_drop_host_(ink_drop_host), - ink_shown_(ink_shown), - ink_hidden_(ink_hidden) { - ink_drop_host_->AddInkDropLayer(nullptr); - } - ~TestInkDropDelegate() override {} + TestInkDropDelegateThatTracksVisibilty(bool* ink_shown, bool* ink_hidden) + : ink_shown_(ink_shown), ink_hidden_(ink_hidden) {} + ~TestInkDropDelegateThatTracksVisibilty() override {} // InkDropDelegate: - void SetInkDropSize(int large_size, - int large_corner_radius, - int small_size, - int small_corner_radius) override {} - void OnLayout() override {} void OnAction(InkDropState state) override { switch (state) { case InkDropState::ACTION_PENDING: - case InkDropState::SLOW_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_PENDING: case InkDropState::ACTIVATED: *ink_shown_ = true; break; case InkDropState::HIDDEN: *ink_hidden_ = true; break; - case InkDropState::QUICK_ACTION: - case InkDropState::SLOW_ACTION: + case InkDropState::ACTION_TRIGGERED: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: case InkDropState::DEACTIVATED: break; } } + void SnapToActivated() override { *ink_shown_ = true; } + + void SetHovered(bool is_hovered) override {} + private: - InkDropHost* ink_drop_host_; bool* ink_shown_; bool* ink_hidden_; - DISALLOW_COPY_AND_ASSIGN(TestInkDropDelegate); + DISALLOW_COPY_AND_ASSIGN(TestInkDropDelegateThatTracksVisibilty); }; // A test Button class that owns a TestInkDropDelegate. class TestButtonWithInkDrop : public TestCustomButton { public: - TestButtonWithInkDrop(bool* ink_shown, bool* ink_hidden) - : TestCustomButton(), - ink_drop_delegate_( - new TestInkDropDelegate(this, ink_shown, ink_hidden)) { + TestButtonWithInkDrop(scoped_ptr<InkDropDelegate> ink_drop_delegate) + : TestCustomButton(), ink_drop_delegate_(std::move(ink_drop_delegate)) { set_ink_drop_delegate(ink_drop_delegate_.get()); } ~TestButtonWithInkDrop() override {} - // views::InkDropHost: - void AddInkDropLayer(ui::Layer* ink_drop_layer) override {} - void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override {} - gfx::Point CalculateInkDropCenter() const override { return gfx::Point(); } - private: scoped_ptr<views::InkDropDelegate> ink_drop_delegate_; DISALLOW_COPY_AND_ASSIGN(TestButtonWithInkDrop); }; +} // namespace + class CustomButtonTest : public ViewsTestBase { public: CustomButtonTest() {} @@ -155,42 +163,34 @@ class CustomButtonTest : public ViewsTestBase { ViewsTestBase::TearDown(); } - void CreateButtonWithInkDrop() { + void CreateButtonWithInkDrop(scoped_ptr<InkDropDelegate> ink_drop_delegate) { delete button_; - ink_shown_ = false; - ink_hidden_ = false; - button_ = new TestButtonWithInkDrop(&ink_shown_, &ink_hidden_); + button_ = new TestButtonWithInkDrop(std::move(ink_drop_delegate)); widget_->SetContentsView(button_); } protected: Widget* widget() { return widget_.get(); } TestCustomButton* button() { return button_; } - bool ink_shown() const { return ink_shown_; } - bool ink_hidden() const { return ink_hidden_; } + void SetDraggedView(View* dragged_view) { + widget_->dragged_view_ = dragged_view; + } private: scoped_ptr<Widget> widget_; TestCustomButton* button_; - bool ink_shown_ = false; - bool ink_hidden_ = false; DISALLOW_COPY_AND_ASSIGN(CustomButtonTest); }; -} // namespace - // Tests that hover state changes correctly when visiblity/enableness changes. TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { - gfx::Point center(10, 10); - button()->OnMousePressed(ui::MouseEvent( - ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); + + generator.PressLeftButton(); EXPECT_EQ(CustomButton::STATE_PRESSED, button()->state()); - button()->OnMouseReleased(ui::MouseEvent( - ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); + generator.ReleaseLeftButton(); EXPECT_EQ(CustomButton::STATE_HOVERED, button()->state()); button()->SetEnabled(false); @@ -372,7 +372,7 @@ TEST_F(CustomButtonTest, AsCustomButton) { RadioButton radio_button(text, 0); EXPECT_TRUE(CustomButton::AsCustomButton(&radio_button)); - MenuButton menu_button(NULL, text, NULL, false); + MenuButton menu_button(text, NULL, false); EXPECT_TRUE(CustomButton::AsCustomButton(&menu_button)); Label label; @@ -390,35 +390,93 @@ TEST_F(CustomButtonTest, AsCustomButton) { // Note: Ink drop is not hidden upon release because CustomButton descendants // may enter a different ink drop state. TEST_F(CustomButtonTest, ButtonClickTogglesInkDrop) { - gfx::Point old_cursor = gfx::Screen::GetScreenFor( - widget()->GetNativeView())->GetCursorScreenPoint(); - CreateButtonWithInkDrop(); + gfx::Point old_cursor = gfx::Screen::GetScreen()->GetCursorScreenPoint(); + bool ink_shown = false; + bool ink_hidden = false; + CreateButtonWithInkDrop(make_scoped_ptr( + new TestInkDropDelegateThatTracksVisibilty(&ink_shown, &ink_hidden))); ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); generator.set_current_location(gfx::Point(50, 50)); generator.PressLeftButton(); - EXPECT_TRUE(ink_shown()); - EXPECT_FALSE(ink_hidden()); + EXPECT_TRUE(ink_shown); + EXPECT_FALSE(ink_hidden); generator.ReleaseLeftButton(); - EXPECT_FALSE(ink_hidden()); + EXPECT_FALSE(ink_hidden); } // 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) { - gfx::Point old_cursor = gfx::Screen::GetScreenFor( - widget()->GetNativeView())->GetCursorScreenPoint(); - CreateButtonWithInkDrop(); + gfx::Point old_cursor = gfx::Screen::GetScreen()->GetCursorScreenPoint(); + bool ink_shown = false; + bool ink_hidden = false; + CreateButtonWithInkDrop(make_scoped_ptr( + new TestInkDropDelegateThatTracksVisibilty(&ink_shown, &ink_hidden))); ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); generator.set_current_location(gfx::Point(50, 50)); generator.PressLeftButton(); - EXPECT_TRUE(ink_shown()); - EXPECT_FALSE(ink_hidden()); + EXPECT_TRUE(ink_shown); + EXPECT_FALSE(ink_hidden); + EXPECT_EQ(Button::ButtonState::STATE_PRESSED, button()->state()); + SetDraggedView(button()); widget()->SetCapture(button()); widget()->ReleaseCapture(); - EXPECT_TRUE(ink_hidden()); + SetDraggedView(nullptr); + EXPECT_TRUE(ink_hidden); + EXPECT_EQ(ui::MaterialDesignController::IsModeMaterial() + ? Button::ButtonState::STATE_NORMAL + : Button::ButtonState::STATE_PRESSED, + button()->state()); +} + +TEST_F(CustomButtonTest, HideInkDropWhenShowingContextMenu) { + TestInkDropDelegate* ink_drop_delegate = new TestInkDropDelegate(); + CreateButtonWithInkDrop(make_scoped_ptr(ink_drop_delegate)); + TestContextMenuController context_menu_controller; + button()->set_context_menu_controller(&context_menu_controller); + button()->set_hide_ink_drop_when_showing_context_menu(true); + + ink_drop_delegate->SetHovered(true); + ink_drop_delegate->OnAction(InkDropState::ACTION_PENDING); + + button()->ShowContextMenu(gfx::Point(), ui::MENU_SOURCE_MOUSE); + + EXPECT_FALSE(ink_drop_delegate->is_hovered()); + EXPECT_EQ(InkDropState::HIDDEN, ink_drop_delegate->state()); +} + +TEST_F(CustomButtonTest, DontHideInkDropWhenShowingContextMenu) { + TestInkDropDelegate* ink_drop_delegate = new TestInkDropDelegate(); + CreateButtonWithInkDrop(make_scoped_ptr(ink_drop_delegate)); + TestContextMenuController context_menu_controller; + button()->set_context_menu_controller(&context_menu_controller); + button()->set_hide_ink_drop_when_showing_context_menu(false); + + ink_drop_delegate->SetHovered(true); + ink_drop_delegate->OnAction(InkDropState::ACTION_PENDING); + + button()->ShowContextMenu(gfx::Point(), ui::MENU_SOURCE_MOUSE); + + EXPECT_TRUE(ink_drop_delegate->is_hovered()); + EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop_delegate->state()); +} + +TEST_F(CustomButtonTest, InkDropAfterTryingToShowContextMenu) { + TestInkDropDelegate* ink_drop_delegate = new TestInkDropDelegate(); + CreateButtonWithInkDrop(make_scoped_ptr(ink_drop_delegate)); + button()->set_context_menu_controller(nullptr); + + ink_drop_delegate->SetHovered(true); + ink_drop_delegate->OnAction(InkDropState::ACTION_PENDING); + + button()->ShowContextMenu(gfx::Point(), ui::MENU_SOURCE_MOUSE); + + EXPECT_TRUE(ink_drop_delegate->is_hovered()); + EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop_delegate->state()); } } // namespace views diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc index e9d4f36c92b..a79341914de 100644 --- a/chromium/ui/views/controls/button/image_button.cc +++ b/chromium/ui/views/controls/button/image_button.cc @@ -47,9 +47,11 @@ const gfx::ImageSkia& ImageButton::GetImage(ButtonState state) const { return images_[state]; } -void ImageButton::SetImage(ButtonState state, const gfx::ImageSkia* image) { - images_[state] = image ? *image : gfx::ImageSkia(); +void ImageButton::SetImage(ButtonState for_state, const gfx::ImageSkia* image) { + images_[for_state] = image ? *image : gfx::ImageSkia(); PreferredSizeChanged(); + if (state() == for_state) + SchedulePaint(); } void ImageButton::SetBackground(SkColor color, @@ -134,13 +136,13 @@ void ImageButton::OnPaint(gfx::Canvas* canvas) { // ImageButton, protected: void ImageButton::OnFocus() { - View::OnFocus(); + CustomButton::OnFocus(); if (focus_painter_.get()) SchedulePaint(); } void ImageButton::OnBlur() { - View::OnBlur(); + CustomButton::OnBlur(); if (focus_painter_.get()) SchedulePaint(); } diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc index 978a5afb6e4..6687b6e03c6 100644 --- a/chromium/ui/views/controls/button/label_button.cc +++ b/chromium/ui/views/controls/button/label_button.cc @@ -10,6 +10,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "build/build_config.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/gfx/canvas.h" @@ -17,6 +18,8 @@ #include "ui/gfx/font_list.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/animation/flood_fill_ink_drop_animation.h" +#include "ui/views/animation/ink_drop_hover.h" #include "ui/views/background.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/painter.h" @@ -51,6 +54,21 @@ const gfx::FontList& GetDefaultBoldFontList() { return font_list.Get(); } +// Ink drop container view that does not capture any events. +class InkDropContainerView : public views::View { + public: + InkDropContainerView() {} + + // View: + bool CanProcessEventsWithinSubtree() const override { + // Ensure the container View is found as the EventTarget instead of this. + return false; + } + + private: + DISALLOW_COPY_AND_ASSIGN(InkDropContainerView); +}; + } // namespace namespace views { @@ -64,6 +82,7 @@ LabelButton::LabelButton(ButtonListener* listener, const base::string16& text) : CustomButton(listener), image_(new ImageView()), label_(new Label()), + ink_drop_container_(new InkDropContainerView()), cached_normal_font_list_(GetDefaultNormalFontList()), cached_bold_font_list_(GetDefaultBoldFontList()), button_state_images_(), @@ -75,7 +94,12 @@ LabelButton::LabelButton(ButtonListener* listener, const base::string16& text) image_label_spacing_(kSpacing), horizontal_alignment_(gfx::ALIGN_LEFT) { SetAnimationDuration(kHoverAnimationDurationMs); - SetText(text); + SetTextInternal(text); + + AddChildView(ink_drop_container_); + ink_drop_container_->SetPaintToLayer(true); + ink_drop_container_->layer()->SetFillsBoundsOpaquely(false); + ink_drop_container_->SetVisible(false); AddChildView(image_); image_->set_interactive(false); @@ -108,8 +132,7 @@ const base::string16& LabelButton::GetText() const { } void LabelButton::SetText(const base::string16& text) { - SetAccessibleName(text); - label_->SetText(text); + SetTextInternal(text); } void LabelButton::SetTextColor(ButtonState for_state, SkColor color) { @@ -143,10 +166,7 @@ void LabelButton::SetFontList(const gfx::FontList& font_list) { cached_normal_font_list_ = font_list; cached_bold_font_list_ = font_list.DeriveWithStyle( font_list.GetFontStyle() | gfx::Font::BOLD); - - // STYLE_BUTTON uses bold text to indicate default buttons. - label_->SetFontList( - style_ == STYLE_BUTTON && is_default_ ? + label_->SetFontList(is_default_ ? cached_bold_font_list_ : cached_normal_font_list_); } @@ -171,22 +191,17 @@ void LabelButton::SetMaxSize(const gfx::Size& max_size) { } void LabelButton::SetIsDefault(bool is_default) { + DCHECK_EQ(STYLE_BUTTON, style_); if (is_default == is_default_) return; + is_default_ = is_default; ui::Accelerator accel(ui::VKEY_RETURN, ui::EF_NONE); is_default_ ? AddAccelerator(accel) : RemoveAccelerator(accel); - // STYLE_BUTTON uses bold text to indicate default buttons. - if (style_ == STYLE_BUTTON) { - label_->SetFontList( - is_default ? cached_bold_font_list_ : cached_normal_font_list_); - // Usually this function is called before |this| is attached to a widget, - // but in the cases where |this| is already shown, we need to re-layout - // because font boldness affects the label's size. - if (GetWidget()) - Layout(); - } + label_->SetFontList( + is_default ? cached_bold_font_list_ : cached_normal_font_list_); + InvalidateLayout(); } void LabelButton::SetStyle(ButtonStyle style) { @@ -282,6 +297,8 @@ int LabelButton::GetHeightForWidth(int w) const { } void LabelButton::Layout() { + ink_drop_container_->SetBoundsRect(GetLocalBounds()); + // By default, GetChildAreaBounds() ignores the top and bottom border, but we // want the image to respect it. gfx::Rect child_area(GetChildAreaBounds()); @@ -366,13 +383,13 @@ void LabelButton::OnPaint(gfx::Canvas* canvas) { } void LabelButton::OnFocus() { - View::OnFocus(); + CustomButton::OnFocus(); // Typically the border renders differently when focused. SchedulePaint(); } void LabelButton::OnBlur() { - View::OnBlur(); + CustomButton::OnBlur(); // Typically the border renders differently when focused. SchedulePaint(); } @@ -384,6 +401,44 @@ void LabelButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { InvalidateLayout(); } +void LabelButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { + image()->SetPaintToLayer(true); + image()->layer()->SetFillsBoundsOpaquely(false); + ink_drop_container_->SetVisible(true); + ink_drop_container_->layer()->Add(ink_drop_layer); +} + +void LabelButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { + image()->SetPaintToLayer(false); + ink_drop_container_->layer()->Remove(ink_drop_layer); + ink_drop_container_->SetVisible(false); +} + +scoped_ptr<views::InkDropAnimation> LabelButton::CreateInkDropAnimation() + const { + return GetText().empty() + ? CustomButton::CreateInkDropAnimation() + : make_scoped_ptr(new views::FloodFillInkDropAnimation( + GetLocalBounds(), GetInkDropCenter(), + GetInkDropBaseColor())); +} + +scoped_ptr<views::InkDropHover> LabelButton::CreateInkDropHover() const { + if (!ShouldShowInkDropHover()) + return nullptr; + return GetText().empty() ? CustomButton::CreateInkDropHover() + : make_scoped_ptr(new views::InkDropHover( + size(), kInkDropSmallCornerRadius, + GetInkDropCenter(), GetInkDropBaseColor())); +} + +gfx::Point LabelButton::GetInkDropCenter() const { + // TODO(bruthig): Make the flood fill ink drops centered on the LocatedEvent + // that triggered them. + return GetText().empty() ? image()->GetMirroredBounds().CenterPoint() + : CustomButton::GetInkDropCenter(); +} + void LabelButton::StateChanged() { const gfx::Size previous_image_size(image_->GetPreferredSize()); UpdateImage(); @@ -468,6 +523,11 @@ void LabelButton::UpdateThemedBorder() { border_is_themed_border_ = true; } +void LabelButton::SetTextInternal(const base::string16& text) { + SetAccessibleName(text); + label_->SetText(text); +} + void LabelButton::ChildPreferredSizeChanged(View* child) { ResetCachedPreferredSize(); PreferredSizeChanged(); diff --git a/chromium/ui/views/controls/button/label_button.h b/chromium/ui/views/controls/button/label_button.h index 2d69ac5a4c4..a8f419c96fa 100644 --- a/chromium/ui/views/controls/button/label_button.h +++ b/chromium/ui/views/controls/button/label_button.h @@ -18,6 +18,8 @@ namespace views { +class InkDropAnimation; +class InkDropHover; class LabelButtonBorder; class Painter; @@ -43,7 +45,7 @@ class VIEWS_EXPORT LabelButton : public CustomButton, // Gets or sets the text shown on the button. const base::string16& GetText() const; - void SetText(const base::string16& text); + virtual void SetText(const base::string16& text); // Sets the text color shown for the specified button |for_state| to |color|. void SetTextColor(ButtonState for_state, SkColor color); @@ -99,6 +101,11 @@ class VIEWS_EXPORT LabelButton : public CustomButton, void Layout() override; const char* GetClassName() const override; void EnableCanvasFlippingForRTLUI(bool flip) override; + void AddInkDropLayer(ui::Layer* ink_drop_layer) override; + void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; + scoped_ptr<InkDropAnimation> CreateInkDropAnimation() const override; + scoped_ptr<InkDropHover> CreateInkDropHover() const override; + gfx::Point GetInkDropCenter() const override; protected: ImageView* image() const { return image_; } @@ -141,6 +148,8 @@ class VIEWS_EXPORT LabelButton : public CustomButton, FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, FontList); FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, ButtonStyleIsDefaultSize); + void SetTextInternal(const base::string16& text); + // View: void ChildPreferredSizeChanged(View* child) override; @@ -162,6 +171,11 @@ class VIEWS_EXPORT LabelButton : public CustomButton, ImageView* image_; Label* label_; + // A separate view is necessary to hold the ink drop layer so that it can + // be stacked below |image_| and on top of |label_|, without resorting to + // drawing |label_| on a layer (which can mess with subpixel anti-aliasing). + View* ink_drop_container_; + // The cached font lists in the normal and bold style. gfx::FontList cached_normal_font_list_; gfx::FontList cached_bold_font_list_; diff --git a/chromium/ui/views/controls/button/label_button_border.cc b/chromium/ui/views/controls/button/label_button_border.cc index ab7140821bf..e8347bb9c79 100644 --- a/chromium/ui/views/controls/button/label_button_border.cc +++ b/chromium/ui/views/controls/button/label_button_border.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "third_party/skia/include/core/SkPaint.h" -#include "third_party/skia/include/effects/SkLerpXfermode.h" +#include "third_party/skia/include/effects/SkArithmeticMode.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/animation/animation.h" #include "ui/gfx/canvas.h" @@ -69,13 +69,10 @@ gfx::Size LabelButtonBorder::GetMinimumSize() const { } LabelButtonAssetBorder::LabelButtonAssetBorder(Button::ButtonStyle style) { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - const gfx::Insets insets(kButtonInsets, - kButtonInsets, - kButtonInsets, - kButtonInsets); - set_insets(GetDefaultInsetsForStyle(style)); + + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + const gfx::Insets insets(kButtonInsets); if (style == Button::STYLE_BUTTON) { SetPainter(false, Button::STATE_NORMAL, Painter::CreateImagePainter( @@ -116,9 +113,9 @@ gfx::Insets LabelButtonAssetBorder::GetDefaultInsetsForStyle( Button::ButtonStyle style) { gfx::Insets insets; if (style == Button::STYLE_BUTTON) { - insets = gfx::Insets(8, 13, 8, 13); + insets = gfx::Insets(8, 13); } else if (style == Button::STYLE_TEXTBUTTON) { - insets = gfx::Insets(5, 6, 5, 6); + insets = gfx::Insets(5, 6); } else { NOTREACHED(); } @@ -148,9 +145,8 @@ void LabelButtonAssetBorder::Paint(const View& view, gfx::Canvas* canvas) { PaintHelper(this, canvas, state, rect, extra); SkPaint paint; - skia::RefPtr<SkXfermode> sk_lerp_xfer = - skia::AdoptRef(SkLerpXfermode::Create(animation->GetCurrentValue())); - paint.setXfermode(sk_lerp_xfer.get()); + double scale = animation->GetCurrentValue(); + paint.setXfermode(SkArithmeticMode::Make(0.0f, scale, 1.0 - scale, 0.0)); canvas->sk_canvas()->saveLayer(&sk_rect, &paint); state = native_theme_delegate->GetForegroundThemeState(&extra); PaintHelper(this, canvas, state, rect, extra); diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc index ef8f52dc6d8..d5b60451e9f 100644 --- a/chromium/ui/views/controls/button/label_button_unittest.cc +++ b/chromium/ui/views/controls/button/label_button_unittest.cc @@ -8,10 +8,16 @@ #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/base/material_design/material_design_controller.h" +#include "ui/base/test/material_design_controller_test_api.h" +#include "ui/events/test/event_generator.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font_list.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/text_utils.h" +#include "ui/views/animation/button_ink_drop_delegate.h" +#include "ui/views/test/views_test_base.h" #include "ui/views/test/widget_test.h" using base::ASCIIToUTF16; @@ -301,4 +307,115 @@ TEST_F(LabelButtonTest, ButtonStyleIsDefaultSize) { EXPECT_NE(non_default_size, button->label_->size()); } +// A ButtonInkDropDelegate that tracks the last hover state requested. +class TestButtonInkDropDelegate : public ButtonInkDropDelegate { + public: + TestButtonInkDropDelegate(InkDropHost* ink_drop_host, View* view) + : ButtonInkDropDelegate(ink_drop_host, view), is_hovered_(false) {} + + ~TestButtonInkDropDelegate() override {} + + bool is_hovered() const { return is_hovered_; } + + // ButtonInkDropDelegate: + void SetHovered(bool is_hovered) override { + is_hovered_ = is_hovered; + ButtonInkDropDelegate::SetHovered(is_hovered); + } + + private: + // The last |is_hover| value passed to SetHovered(). + bool is_hovered_; + + DISALLOW_COPY_AND_ASSIGN(TestButtonInkDropDelegate); +}; + +// A generic LabelButton configured with an |InkDropDelegate|. +class InkDropLabelButton : public LabelButton { + public: + InkDropLabelButton() + : LabelButton(nullptr, base::string16()), + test_ink_drop_delegate_(this, this) { + set_ink_drop_delegate(&test_ink_drop_delegate_); + } + + ~InkDropLabelButton() override {} + + TestButtonInkDropDelegate* test_ink_drop_delegate() { + return &test_ink_drop_delegate_; + } + + private: + TestButtonInkDropDelegate test_ink_drop_delegate_; + + DISALLOW_COPY_AND_ASSIGN(InkDropLabelButton); +}; + +// Test fixture for a LabelButton that has an ink drop configured. +class InkDropLabelButtonTest : public ViewsTestBase { + public: + InkDropLabelButtonTest() {} + + // ViewsTestBase: + void SetUp() override { + ui::test::MaterialDesignControllerTestAPI::SetMode( + ui::MaterialDesignController::MATERIAL_NORMAL); + + ViewsTestBase::SetUp(); + + // Create a widget so that the CustomButton can query the hover state + // correctly. + widget_.reset(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 20, 20); + widget_->Init(params); + widget_->Show(); + + button_ = new InkDropLabelButton(); + widget_->SetContentsView(button_); + } + + void TearDown() override { + widget_.reset(); + ViewsTestBase::TearDown(); + ui::test::MaterialDesignControllerTestAPI::UninitializeMode(); + } + + protected: + // Required to host the test target. + scoped_ptr<Widget> widget_; + + // The test target. + InkDropLabelButton* button_ = nullptr; + + private: + DISALLOW_COPY_AND_ASSIGN(InkDropLabelButtonTest); +}; + +TEST_F(InkDropLabelButtonTest, HoverStateAfterMouseEnterAndExitEvents) { + ui::test::EventGenerator event_generator(GetContext(), + widget_->GetNativeWindow()); + const gfx::Point out_of_bounds_point(button_->bounds().bottom_right() + + gfx::Vector2d(1, 1)); + const gfx::Point in_bounds_point(button_->bounds().CenterPoint()); + + event_generator.MoveMouseTo(out_of_bounds_point); + EXPECT_FALSE(button_->test_ink_drop_delegate()->is_hovered()); + + event_generator.MoveMouseTo(in_bounds_point); + EXPECT_TRUE(button_->test_ink_drop_delegate()->is_hovered()); + + event_generator.MoveMouseTo(out_of_bounds_point); + EXPECT_FALSE(button_->test_ink_drop_delegate()->is_hovered()); +} + +// Verifies the target event handler View is the |LabelButton| and not any of +// the child Views. +TEST_F(InkDropLabelButtonTest, TargetEventHandler) { + View* target_view = widget_->GetRootView()->GetEventHandlerForPoint( + button_->bounds().CenterPoint()); + EXPECT_EQ(button_, target_view); +} + } // namespace views diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc index 4cae74f7dfa..bf0c9bd03dd 100644 --- a/chromium/ui/views/controls/button/md_text_button.cc +++ b/chromium/ui/views/controls/button/md_text_button.cc @@ -5,8 +5,12 @@ #include "ui/views/controls/button/md_text_button.h" #include "base/i18n/case_conversion.h" -#include "ui/gfx/render_text.h" +#include "ui/base/material_design/material_design_controller.h" +#include "ui/gfx/color_utils.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/background.h" +#include "ui/views/border.h" +#include "ui/views/painter.h" namespace views { @@ -19,47 +23,92 @@ const int kVerticalPadding = 6; // Minimum size to reserve for the button contents. const int kMinWidth = 48; -const gfx::FontList& GetFontList() { - static base::LazyInstance<gfx::FontList>::Leaky font_list = - LAZY_INSTANCE_INITIALIZER; - return font_list.Get(); +} // namespace + +// static +LabelButton* MdTextButton::CreateStandardButton(ButtonListener* listener, + const base::string16& text) { + if (ui::MaterialDesignController::IsModeMaterial()) + return CreateMdButton(listener, text); + + LabelButton* button = new LabelButton(listener, text); + button->SetStyle(STYLE_BUTTON); + return button; } -} // namespace +MdTextButton* MdTextButton::CreateMdButton(ButtonListener* listener, + const base::string16& text) { + MdTextButton* button = new MdTextButton(listener); + button->SetText(text); + // TODO(estade): can we get rid of the platform style border hoopla if + // we apply the MD treatment to all buttons, even GTK buttons? + button->SetBorder( + Border::CreateEmptyBorder(kVerticalPadding, kHorizontalPadding, + kVerticalPadding, kHorizontalPadding)); + return button; +} -MdTextButton::MdTextButton(ButtonListener* listener, const base::string16& text) - : CustomButton(listener), - render_text_(gfx::RenderText::CreateInstance()) { - render_text_->SetFontList(GetFontList()); - render_text_->SetCursorEnabled(false); - render_text_->SetText(base::i18n::ToUpper(text)); +void MdTextButton::SetCallToAction(CallToAction cta) { + if (cta_ == cta) + return; - SetFocusable(true); + cta_ = cta; + UpdateColorsFromNativeTheme(); } -MdTextButton::~MdTextButton() {} +void MdTextButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { + LabelButton::OnNativeThemeChanged(theme); + UpdateColorsFromNativeTheme(); +} + +SkColor MdTextButton::GetInkDropBaseColor() const { + return color_utils::DeriveDefaultIconColor(label()->enabled_color()); +} -void MdTextButton::OnPaint(gfx::Canvas* canvas) { - UpdateColor(); - gfx::Rect rect = GetLocalBounds(); - rect.Inset(kHorizontalPadding, kVerticalPadding); - render_text_->SetDisplayRect(rect); - render_text_->Draw(canvas); +void MdTextButton::SetText(const base::string16& text) { + LabelButton::SetText(base::i18n::ToUpper(text)); } -gfx::Size MdTextButton::GetPreferredSize() const { - gfx::Size size = render_text_->GetStringSize(); - size.SetToMax(gfx::Size(kMinWidth, 0)); - size.Enlarge(kHorizontalPadding * 2, kVerticalPadding * 2); - return size; +MdTextButton::MdTextButton(ButtonListener* listener) + : LabelButton(listener, base::string16()), + ink_drop_delegate_(this, this), + cta_(NO_CALL_TO_ACTION) { + set_ink_drop_delegate(&ink_drop_delegate_); + set_has_ink_drop_action_on_click(true); + SetHorizontalAlignment(gfx::ALIGN_CENTER); + SetFocusable(true); + SetMinSize(gfx::Size(kMinWidth, 0)); + SetFocusPainter(nullptr); + UseMdFocusRing(); + label()->SetAutoColorReadabilityEnabled(false); } -void MdTextButton::UpdateColor() { - // TODO(estade): handle call to action theming and other things that can - // affect the text color. - render_text_->SetColor(GetNativeTheme()->GetSystemColor( - enabled() ? ui::NativeTheme::kColorId_MdTextButtonEnabledColor - : ui::NativeTheme::kColorId_MdTextButtonDisabledColor)); +MdTextButton::~MdTextButton() {} + +void MdTextButton::UpdateColorsFromNativeTheme() { + ui::NativeTheme::ColorId fg_color_id = ui::NativeTheme::kColorId_NumColors; + switch (cta_) { + case NO_CALL_TO_ACTION: + fg_color_id = ui::NativeTheme::kColorId_ButtonEnabledColor; + break; + case WEAK_CALL_TO_ACTION: + fg_color_id = ui::NativeTheme::kColorId_CallToActionColor; + break; + case STRONG_CALL_TO_ACTION: + fg_color_id = ui::NativeTheme::kColorId_TextOnCallToActionColor; + break; + } + ui::NativeTheme* theme = GetNativeTheme(); + SetEnabledTextColors(theme->GetSystemColor(fg_color_id)); + + set_background( + cta_ == STRONG_CALL_TO_ACTION + ? Background::CreateBackgroundPainter( + true, Painter::CreateSolidRoundRectPainter( + theme->GetSystemColor( + ui::NativeTheme::kColorId_CallToActionColor), + kInkDropSmallCornerRadius)) + : nullptr); } } // namespace views diff --git a/chromium/ui/views/controls/button/md_text_button.h b/chromium/ui/views/controls/button/md_text_button.h index ba982a32a93..8350dac7c68 100644 --- a/chromium/ui/views/controls/button/md_text_button.h +++ b/chromium/ui/views/controls/button/md_text_button.h @@ -6,28 +6,46 @@ #define UI_VIEWS_CONTROLS_BUTTON_MD_TEXT_BUTTON_H_ #include "base/memory/scoped_ptr.h" -#include "ui/views/controls/button/custom_button.h" - -namespace gfx { -class RenderText; -} +#include "ui/views/animation/button_ink_drop_delegate.h" +#include "ui/views/controls/button/label_button.h" namespace views { // A button class that implements the Material Design text button spec. -class VIEWS_EXPORT MdTextButton : public CustomButton { +class VIEWS_EXPORT MdTextButton : public LabelButton { public: - MdTextButton(ButtonListener* listener, const base::string16& text); + // Describes the presentation of a button. A stronger call to action draws + // more attention. + enum CallToAction { + NO_CALL_TO_ACTION, // Default. + WEAK_CALL_TO_ACTION, + STRONG_CALL_TO_ACTION, + }; + + // Creates a normal STYLE_BUTTON LabelButton in pre-MD, or an MdTextButton + // in MD mode. + static LabelButton* CreateStandardButton(ButtonListener* listener, + const base::string16& text); + static MdTextButton* CreateMdButton(ButtonListener* listener, + const base::string16& text); + + void SetCallToAction(CallToAction cta); + + // LabelButton: + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + SkColor GetInkDropBaseColor() const override; + void SetText(const base::string16& text) override; + + private: + MdTextButton(ButtonListener* listener); ~MdTextButton() override; - // View: - void OnPaint(gfx::Canvas* canvas) override; - gfx::Size GetPreferredSize() const override; + void UpdateColorsFromNativeTheme(); - private: - void UpdateColor(); + ButtonInkDropDelegate ink_drop_delegate_; - scoped_ptr<gfx::RenderText> render_text_; + // The call to action style for this button. + CallToAction cta_; DISALLOW_COPY_AND_ASSIGN(MdTextButton); }; diff --git a/chromium/ui/views/controls/button/menu_button.cc b/chromium/ui/views/controls/button/menu_button.cc index c7a53389a14..4d1a1b97281 100644 --- a/chromium/ui/views/controls/button/menu_button.cc +++ b/chromium/ui/views/controls/button/menu_button.cc @@ -47,8 +47,12 @@ const int MenuButton::kMenuMarkerPaddingRight = -1; //////////////////////////////////////////////////////////////////////////////// MenuButton::PressedLock::PressedLock(MenuButton* menu_button) + : PressedLock(menu_button, false) {} + +MenuButton::PressedLock::PressedLock(MenuButton* menu_button, + bool is_sibling_menu_show) : menu_button_(menu_button->weak_factory_.GetWeakPtr()) { - menu_button_->IncrementPressedLocked(); + menu_button_->IncrementPressedLocked(is_sibling_menu_show); } MenuButton::PressedLock::~PressedLock() { @@ -62,18 +66,19 @@ MenuButton::PressedLock::~PressedLock() { // //////////////////////////////////////////////////////////////////////////////// -MenuButton::MenuButton(ButtonListener* listener, - const base::string16& text, +MenuButton::MenuButton(const base::string16& text, MenuButtonListener* menu_button_listener, bool show_menu_marker) - : LabelButton(listener, text), + : LabelButton(nullptr, text), menu_offset_(kDefaultMenuOffsetX, kDefaultMenuOffsetY), listener_(menu_button_listener), show_menu_marker_(show_menu_marker), - menu_marker_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( - IDR_MENU_DROPARROW).ToImageSkia()), - destroyed_flag_(NULL), + menu_marker_(ui::ResourceBundle::GetSharedInstance() + .GetImageNamed(IDR_MENU_DROPARROW) + .ToImageSkia()), + destroyed_flag_(nullptr), pressed_lock_count_(0), + increment_pressed_lock_called_(nullptr), should_disable_after_press_(false), weak_factory_(this) { SetHorizontalAlignment(gfx::ALIGN_LEFT); @@ -90,7 +95,7 @@ MenuButton::~MenuButton() { // //////////////////////////////////////////////////////////////////////////////// -bool MenuButton::Activate() { +bool MenuButton::Activate(const ui::Event* event) { if (listener_) { gfx::Rect lb = GetLocalBounds(); @@ -117,36 +122,67 @@ bool MenuButton::Activate() { // matter where the user pressed. To force RootView to recalculate the // mouse target during the mouse press we explicitly set the mouse handler // to NULL. - static_cast<internal::RootView*>(GetWidget()->GetRootView())-> - SetMouseHandler(NULL); + static_cast<internal::RootView*>(GetWidget()->GetRootView()) + ->SetMouseHandler(nullptr); bool destroyed = false; destroyed_flag_ = &destroyed; + DCHECK(increment_pressed_lock_called_ == nullptr); + // Observe if IncrementPressedLocked() was called so we can trigger the + // correct ink drop animations. + bool increment_pressed_lock_called = false; + increment_pressed_lock_called_ = &increment_pressed_lock_called; + // We don't set our state here. It's handled in the MenuController code or // by our click listener. - - if (ink_drop_delegate()) - ink_drop_delegate()->OnAction(InkDropState::QUICK_ACTION); - listener_->OnMenuButtonClicked(this, menu_position); + listener_->OnMenuButtonClicked(this, menu_position, event); if (destroyed) { // The menu was deleted while showing. Don't attempt any processing. return false; } - destroyed_flag_ = NULL; + increment_pressed_lock_called_ = nullptr; + destroyed_flag_ = nullptr; menu_closed_time_ = TimeTicks::Now(); + if (ink_drop_delegate() && !increment_pressed_lock_called && + pressed_lock_count_ == 0) { + ink_drop_delegate()->OnAction(InkDropState::ACTION_TRIGGERED); + } + // We must return false here so that the RootView does not get stuck // sending all mouse pressed events to us instead of the appropriate // target. return false; } + + if (ink_drop_delegate()) + ink_drop_delegate()->OnAction(InkDropState::HIDDEN); return true; } +bool MenuButton::IsTriggerableEventType(const ui::Event& event) { + if (event.IsMouseEvent()) { + const ui::MouseEvent& mouseev = static_cast<const ui::MouseEvent&>(event); + // Active on left mouse button only, to prevent a menu from being activated + // when a right-click would also activate a context menu. + if (!mouseev.IsOnlyLeftMouseButton()) + return false; + // If dragging is supported activate on release, otherwise activate on + // pressed. + ui::EventType active_on = + GetDragOperations(mouseev.location()) == ui::DragDropTypes::DRAG_NONE + ? ui::ET_MOUSE_PRESSED + : ui::ET_MOUSE_RELEASED; + return event.type() == active_on; + } + + return event.type() == ui::ET_GESTURE_TAP; +} + void MenuButton::OnPaint(gfx::Canvas* canvas) { LabelButton::OnPaint(canvas); @@ -177,21 +213,18 @@ const char* MenuButton::GetClassName() const { bool MenuButton::OnMousePressed(const ui::MouseEvent& event) { if (request_focus_on_press()) RequestFocus(); - if (state() != STATE_DISABLED && ShouldEnterPushedState(event) && - HitTestPoint(event.location())) { - TimeDelta delta = TimeTicks::Now() - menu_closed_time_; - if (delta.InMilliseconds() > kMinimumMsBetweenButtonClicks) - return Activate(); - if (ink_drop_delegate()) - ink_drop_delegate()->OnAction(InkDropState::ACTION_PENDING); + if (state() != STATE_DISABLED && HitTestPoint(event.location()) && + IsTriggerableEventType(event)) { + if (IsTriggerableEvent(event)) + return Activate(&event); } return true; } void MenuButton::OnMouseReleased(const ui::MouseEvent& event) { - if (state() != STATE_DISABLED && ShouldEnterPushedState(event) && + if (state() != STATE_DISABLED && IsTriggerableEvent(event) && HitTestPoint(event.location()) && !InDrag()) { - Activate(); + Activate(&event); } else { if (ink_drop_delegate()) ink_drop_delegate()->OnAction(InkDropState::HIDDEN); @@ -216,7 +249,7 @@ void MenuButton::OnMouseMoved(const ui::MouseEvent& event) { void MenuButton::OnGestureEvent(ui::GestureEvent* event) { if (state() != STATE_DISABLED) { - if (ShouldEnterPushedState(*event) && !Activate()) { + if (IsTriggerableEvent(*event) && !Activate(event)) { // When |Activate()| returns |false|, it means the click was handled by // a button listener and has handled the gesture event. So, there is no // need to further process the gesture event here. However, if the @@ -251,7 +284,7 @@ bool MenuButton::OnKeyPressed(const ui::KeyEvent& event) { case ui::VKEY_UP: case ui::VKEY_DOWN: { // WARNING: we may have been deleted by the time Activate returns. - Activate(); + Activate(&event); // This is to prevent the keyboard event from being dispatched twice. If // the keyboard event is not handled, we pass it to the default handler // which dispatches the event back to us causing the menu to get displayed @@ -271,13 +304,6 @@ bool MenuButton::OnKeyReleased(const ui::KeyEvent& event) { return false; } -bool MenuButton::AcceleratorPressed(const ui::Accelerator& accelerator) { - // CustomButton::AcceleratorPressed ends up in NotifyClick, which doesn't work - // for menu buttons. - Activate(); - return true; -} - void MenuButton::GetAccessibleState(ui::AXViewState* state) { CustomButton::GetAccessibleState(state); state->role = ui::AX_ROLE_POP_UP_BUTTON; @@ -310,23 +336,19 @@ gfx::Rect MenuButton::GetChildAreaBounds() { return gfx::Rect(s); } -bool MenuButton::ShouldEnterPushedState(const ui::Event& event) { - if (event.IsMouseEvent()) { - const ui::MouseEvent& mouseev = static_cast<const ui::MouseEvent&>(event); - // Active on left mouse button only, to prevent a menu from being activated - // when a right-click would also activate a context menu. - if (!mouseev.IsOnlyLeftMouseButton()) - return false; - // If dragging is supported activate on release, otherwise activate on - // pressed. - ui::EventType active_on = - GetDragOperations(mouseev.location()) == ui::DragDropTypes::DRAG_NONE - ? ui::ET_MOUSE_PRESSED - : ui::ET_MOUSE_RELEASED; - return event.type() == active_on; - } +bool MenuButton::IsTriggerableEvent(const ui::Event& event) { + if (!IsTriggerableEventType(event)) + return false; - return event.type() == ui::ET_GESTURE_TAP; + TimeDelta delta = TimeTicks::Now() - menu_closed_time_; + if (delta.InMilliseconds() < kMinimumMsBetweenButtonClicks) + return false; // Not enough time since the menu closed. + + return true; +} + +bool MenuButton::ShouldEnterPushedState(const ui::Event& event) { + return IsTriggerableEventType(event); } void MenuButton::StateChanged() { @@ -344,9 +366,23 @@ void MenuButton::StateChanged() { } } -void MenuButton::IncrementPressedLocked() { +void MenuButton::NotifyClick(const ui::Event& event) { + // We don't forward events to the normal button listener, instead using the + // MenuButtonListener. + Activate(&event); +} + +void MenuButton::IncrementPressedLocked(bool snap_ink_drop_to_activated) { ++pressed_lock_count_; + if (increment_pressed_lock_called_) + *increment_pressed_lock_called_ = true; should_disable_after_press_ = state() == STATE_DISABLED; + if (state() != STATE_PRESSED && ink_drop_delegate()) { + if (snap_ink_drop_to_activated) + ink_drop_delegate()->SnapToActivated(); + else + ink_drop_delegate()->OnAction(InkDropState::ACTIVATED); + } SetState(STATE_PRESSED); } @@ -364,6 +400,8 @@ void MenuButton::DecrementPressedLocked() { desired_state = STATE_HOVERED; } SetState(desired_state); + if (ink_drop_delegate() && state() != STATE_PRESSED) + ink_drop_delegate()->OnAction(InkDropState::DEACTIVATED); } } diff --git a/chromium/ui/views/controls/button/menu_button.h b/chromium/ui/views/controls/button/menu_button.h index 94c209b96eb..696a94146f3 100644 --- a/chromium/ui/views/controls/button/menu_button.h +++ b/chromium/ui/views/controls/button/menu_button.h @@ -32,6 +32,7 @@ class VIEWS_EXPORT MenuButton : public LabelButton { class VIEWS_EXPORT PressedLock { public: explicit PressedLock(MenuButton* menu_button); + PressedLock(MenuButton* menu_button, bool is_sibling_menu_show); ~PressedLock(); private: @@ -47,8 +48,7 @@ class VIEWS_EXPORT MenuButton : public LabelButton { static const int kMenuMarkerPaddingRight; // Create a Button. - MenuButton(ButtonListener* listener, - const base::string16& text, + MenuButton(const base::string16& text, MenuButtonListener* menu_button_listener, bool show_menu_marker); ~MenuButton() override; @@ -62,8 +62,15 @@ class VIEWS_EXPORT MenuButton : public LabelButton { const gfx::Point& menu_offset() const { return menu_offset_; } void set_menu_offset(int x, int y) { menu_offset_.SetPoint(x, y); } - // Activate the button (called when the button is pressed). - bool Activate(); + // Activate the button (called when the button is pressed). |event| is the + // event triggering the activation, if any. + bool Activate(const ui::Event* event); + + // Returns true if the event is of the proper type to potentially trigger an + // action. Since MenuButtons have properties other than event type (like + // last menu open time) to determine if an event is valid to activate the + // menu, this is distinct from IsTriggerableEvent(). + virtual bool IsTriggerableEventType(const ui::Event& event); // Overridden from View: gfx::Size GetPreferredSize() const override; @@ -77,7 +84,6 @@ class VIEWS_EXPORT MenuButton : public LabelButton { void OnGestureEvent(ui::GestureEvent* event) override; bool OnKeyPressed(const ui::KeyEvent& event) override; bool OnKeyReleased(const ui::KeyEvent& event) override; - bool AcceleratorPressed(const ui::Accelerator& accelerator) override; void GetAccessibleState(ui::AXViewState* state) override; protected: @@ -88,8 +94,10 @@ class VIEWS_EXPORT MenuButton : public LabelButton { gfx::Rect GetChildAreaBounds() override; // Overridden from CustomButton: + bool IsTriggerableEvent(const ui::Event& event) override; bool ShouldEnterPushedState(const ui::Event& event) override; void StateChanged() override; + void NotifyClick(const ui::Event& event) override; // Offset of the associated menu position. gfx::Point menu_offset_; @@ -98,8 +106,10 @@ class VIEWS_EXPORT MenuButton : public LabelButton { friend class PressedLock; // Increment/decrement the number of "pressed" locks this button has, and - // set the state accordingly. - void IncrementPressedLocked(); + // set the state accordingly. The ink drop is snapped to the final ACTIVATED + // state if |snap_ink_drop_to_activated| is true, otherwise the ink drop will + // be animated to the ACTIVATED state. + void IncrementPressedLocked(bool snap_ink_drop_to_activated); void DecrementPressedLocked(); // Compute the maximum X coordinate for the current screen. MenuButtons @@ -130,6 +140,9 @@ class VIEWS_EXPORT MenuButton : public LabelButton { // The current number of "pressed" locks this button has. int pressed_lock_count_; + // Used to let Activate() know if IncrementPressedLocked() was called. + bool* increment_pressed_lock_called_; + // True if the button was in a disabled state when a menu was run, and should // return to it once the press is complete. This can happen if, e.g., we // programmatically show a menu on a disabled button. diff --git a/chromium/ui/views/controls/button/menu_button_listener.h b/chromium/ui/views/controls/button/menu_button_listener.h index 5a20697cffb..d344bb0cf3b 100644 --- a/chromium/ui/views/controls/button/menu_button_listener.h +++ b/chromium/ui/views/controls/button/menu_button_listener.h @@ -11,15 +11,24 @@ namespace gfx { class Point; } -namespace views { +namespace ui { +class Event; +} -class View; +namespace views { +class MenuButton; // An interface implemented by an object to let it know that a menu button was // clicked. class VIEWS_EXPORT MenuButtonListener { public: - virtual void OnMenuButtonClicked(View* source, const gfx::Point& point) = 0; + // Notifies that the MenuButton has been clicked. |point| is the default + // point to display the menu, and |event| is the event causing the click, if + // any. (Note: "Clicked" refers to any activation, including e.g. accelerators + // and key events). + virtual void OnMenuButtonClicked(MenuButton* source, + const gfx::Point& point, + const ui::Event* event) = 0; protected: virtual ~MenuButtonListener() {} diff --git a/chromium/ui/views/controls/button/menu_button_unittest.cc b/chromium/ui/views/controls/button/menu_button_unittest.cc index 8d554c9e5f7..fdfc4d0a9b4 100644 --- a/chromium/ui/views/controls/button/menu_button_unittest.cc +++ b/chromium/ui/views/controls/button/menu_button_unittest.cc @@ -10,6 +10,7 @@ #include "build/build_config.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/events/test/event_generator.h" +#include "ui/views/animation/test/test_ink_drop_delegate.h" #include "ui/views/controls/button/menu_button_listener.h" #include "ui/views/drag_controller.h" #include "ui/views/test/views_test_base.h" @@ -23,6 +24,28 @@ using base::ASCIIToUTF16; namespace views { +class InkDropDelegate; + +namespace test { + +// A MenuButton subclass that provides access to some MenuButton internals. +class TestMenuButton : public MenuButton { + public: + explicit TestMenuButton(MenuButtonListener* menu_button_listener) + : MenuButton(base::string16(ASCIIToUTF16("button")), + menu_button_listener, + false) {} + + ~TestMenuButton() override {} + + // Accessors to protected MenuButton methods. + void set_ink_drop_delegate(InkDropDelegate* ink_drop_delegate) { + MenuButton::set_ink_drop_delegate(ink_drop_delegate); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestMenuButton); +}; class MenuButtonTest : public ViewsTestBase { public: @@ -30,6 +53,7 @@ class MenuButtonTest : public ViewsTestBase { ~MenuButtonTest() override {} void TearDown() override { + generator_.reset(); if (widget_ && !widget_->IsClosed()) widget_->Close(); @@ -37,34 +61,31 @@ class MenuButtonTest : public ViewsTestBase { } Widget* widget() { return widget_; } - MenuButton* button() { return button_; } + TestMenuButton* button() { return button_; } + ui::test::EventGenerator* generator() { return generator_.get(); } protected: // Creates a MenuButton with no button listener. - void CreateMenuButtonWithNoListener() { CreateMenuButton(nullptr, nullptr); } - - // Creates a MenuButton with a ButtonListener. In this case, the MenuButton - // acts like a regular button. - void CreateMenuButtonWithButtonListener(ButtonListener* button_listener) { - CreateMenuButton(button_listener, nullptr); - } + void CreateMenuButtonWithNoListener() { CreateMenuButton(nullptr); } // Creates a MenuButton with a MenuButtonListener. In this case, when the // MenuButton is pushed, it notifies the MenuButtonListener to open a // drop-down menu. void CreateMenuButtonWithMenuButtonListener( MenuButtonListener* menu_button_listener) { - CreateMenuButton(nullptr, menu_button_listener); + CreateMenuButton(menu_button_listener); } private: - void CreateMenuButton(ButtonListener* button_listener, - MenuButtonListener* menu_button_listener) { + void CreateMenuButton(MenuButtonListener* menu_button_listener) { CreateWidget(); + generator_.reset(new ui::test::EventGenerator(GetContext(), + widget_->GetNativeWindow())); + // Set initial mouse location in a consistent way so that the menu button we + // are about to create initializes its hover state in a consistent manner. + generator_->set_current_location(gfx::Point(10, 10)); - const base::string16 label(ASCIIToUTF16("button")); - button_ = - new MenuButton(button_listener, label, menu_button_listener, false); + button_ = new TestMenuButton(menu_button_listener); button_->SetBoundsRect(gfx::Rect(0, 0, 200, 20)); widget_->SetContentsView(button_); @@ -82,7 +103,10 @@ class MenuButtonTest : public ViewsTestBase { } Widget* widget_; - MenuButton* button_; + TestMenuButton* button_; + scoped_ptr<ui::test::EventGenerator> generator_; + + DISALLOW_COPY_AND_ASSIGN(MenuButtonTest); }; class TestButtonListener : public ButtonListener { @@ -119,7 +143,9 @@ class TestMenuButtonListener : public MenuButtonListener { : last_source_(nullptr), last_source_state_(Button::STATE_NORMAL) {} ~TestMenuButtonListener() override {} - void OnMenuButtonClicked(View* source, const gfx::Point& /*point*/) override { + void OnMenuButtonClicked(MenuButton* source, + const gfx::Point& point, + const ui::Event* event) override { last_source_ = source; CustomButton* custom_button = CustomButton::AsCustomButton(source); DCHECK(custom_button); @@ -136,6 +162,36 @@ class TestMenuButtonListener : public MenuButtonListener { DISALLOW_COPY_AND_ASSIGN(TestMenuButtonListener); }; +// A MenuButtonListener that will acquire a PressedLock in the +// OnMenuButtonClicked() method and optionally release it as well. +class PressStateMenuButtonListener : public MenuButtonListener { + public: + explicit PressStateMenuButtonListener(bool release_lock) + : menu_button_(nullptr), release_lock_(release_lock) {} + + ~PressStateMenuButtonListener() override {} + + void set_menu_button(MenuButton* menu_button) { menu_button_ = menu_button; } + + void OnMenuButtonClicked(MenuButton* source, + const gfx::Point& point, + const ui::Event* event) override { + pressed_lock_.reset(new MenuButton::PressedLock(menu_button_)); + if (release_lock_) + pressed_lock_.reset(); + } + + private: + MenuButton* menu_button_; + + scoped_ptr<MenuButton::PressedLock> pressed_lock_; + + // The |pressed_lock_| will be released when true. + bool release_lock_; + + DISALLOW_COPY_AND_ASSIGN(PressStateMenuButtonListener); +}; + // Basic implementation of a DragController, to test input behaviour for // MenuButtons that can be dragged. class TestDragController : public DragController { @@ -257,7 +313,9 @@ class TestShowSiblingButtonListener : public MenuButtonListener { TestShowSiblingButtonListener() {} ~TestShowSiblingButtonListener() override {} - void OnMenuButtonClicked(View* source, const gfx::Point& point) override { + void OnMenuButtonClicked(MenuButton* source, + const gfx::Point& point, + const ui::Event* event) override { // The MenuButton itself doesn't set the PRESSED state during Activate() or // OnMenuButtonClicked(). That should be handled by the MenuController or, // if no menu is shown, the listener. @@ -268,34 +326,13 @@ class TestShowSiblingButtonListener : public MenuButtonListener { DISALLOW_COPY_AND_ASSIGN(TestShowSiblingButtonListener); }; -// Tests if the listener is notified correctly, when a mouse click happens on a -// MenuButton that has a regular ButtonListener. -TEST_F(MenuButtonTest, ActivateNonDropDownOnMouseClick) { - TestButtonListener button_listener; - CreateMenuButtonWithButtonListener(&button_listener); - - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - - generator.set_current_location(gfx::Point(10, 10)); - generator.ClickLeftButton(); - - // Check that MenuButton has notified the listener on mouse-released event, - // while it was in hovered state. - EXPECT_EQ(button(), button_listener.last_sender()); - EXPECT_EQ(ui::ET_MOUSE_RELEASED, button_listener.last_event_type()); - EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_state()); -} - // Tests if the listener is notified correctly when a mouse click happens on a // MenuButton that has a MenuButtonListener. TEST_F(MenuButtonTest, ActivateDropDownOnMouseClick) { TestMenuButtonListener menu_button_listener; CreateMenuButtonWithMenuButtonListener(&menu_button_listener); - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - - generator.set_current_location(gfx::Point(10, 10)); - generator.ClickLeftButton(); + generator()->ClickLeftButton(); // Check that MenuButton has notified the listener, while it was in pressed // state. @@ -304,12 +341,11 @@ TEST_F(MenuButtonTest, ActivateDropDownOnMouseClick) { } // Test that the MenuButton stays pressed while there are any PressedLocks. -TEST_F(MenuButtonTest, MenuButtonPressedLock) { +TEST_F(MenuButtonTest, ButtonStateForMenuButtonsWithPressedLocks) { CreateMenuButtonWithNoListener(); // Move the mouse over the button; the button should be in a hovered state. - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - generator.MoveMouseTo(gfx::Point(10, 10)); + generator()->MoveMouseTo(gfx::Point(10, 10)); EXPECT_EQ(Button::STATE_HOVERED, button()->state()); // Introduce a PressedLock, which should make the button pressed. @@ -318,7 +354,7 @@ TEST_F(MenuButtonTest, MenuButtonPressedLock) { EXPECT_EQ(Button::STATE_PRESSED, button()->state()); // Even if we move the mouse outside of the button, it should remain pressed. - generator.MoveMouseTo(gfx::Point(300, 10)); + generator()->MoveMouseTo(gfx::Point(300, 10)); EXPECT_EQ(Button::STATE_PRESSED, button()->state()); // Creating a new lock should obviously keep the button pressed. @@ -335,7 +371,7 @@ TEST_F(MenuButtonTest, MenuButtonPressedLock) { EXPECT_EQ(Button::STATE_NORMAL, button()->state()); // ...And it should respond to mouse movement again. - generator.MoveMouseTo(gfx::Point(10, 10)); + generator()->MoveMouseTo(gfx::Point(10, 10)); EXPECT_EQ(Button::STATE_HOVERED, button()->state()); // Test that the button returns to the appropriate state after the press; if @@ -353,7 +389,7 @@ TEST_F(MenuButtonTest, MenuButtonPressedLock) { pressed_lock1.reset(); EXPECT_EQ(Button::STATE_DISABLED, button()->state()); - generator.MoveMouseTo(gfx::Point(300, 10)); + generator()->MoveMouseTo(gfx::Point(300, 10)); // Edge case: the button is disabled, a pressed lock is added, and then the // button is re-enabled. It should be enabled after the lock is removed. @@ -371,10 +407,9 @@ TEST_F(MenuButtonTest, PressedStateWithSiblingMenu) { CreateMenuButtonWithMenuButtonListener(&listener); // Move the mouse over the button; the button should be in a hovered state. - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - generator.MoveMouseTo(gfx::Point(10, 10)); + generator()->MoveMouseTo(gfx::Point(10, 10)); EXPECT_EQ(Button::STATE_HOVERED, button()->state()); - generator.ClickLeftButton(); + generator()->ClickLeftButton(); // Test is continued in TestShowSiblingButtonListener::OnMenuButtonClicked(). } @@ -386,17 +421,119 @@ TEST_F(MenuButtonTest, DraggableMenuButtonActivatesOnRelease) { TestDragController drag_controller; button()->set_drag_controller(&drag_controller); - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - - generator.set_current_location(gfx::Point(10, 10)); - generator.PressLeftButton(); + generator()->PressLeftButton(); EXPECT_EQ(nullptr, menu_button_listener.last_source()); - generator.ReleaseLeftButton(); + generator()->ReleaseLeftButton(); EXPECT_EQ(button(), menu_button_listener.last_source()); EXPECT_EQ(Button::STATE_HOVERED, menu_button_listener.last_source_state()); } +TEST_F(MenuButtonTest, InkDropStateForMenuButtonActivationsWithoutListener) { + CreateMenuButtonWithNoListener(); + TestInkDropDelegate ink_drop_delegate; + ink_drop_delegate.OnAction(InkDropState::ACTION_PENDING); + button()->set_ink_drop_delegate(&ink_drop_delegate); + button()->Activate(nullptr); + + EXPECT_EQ(InkDropState::HIDDEN, ink_drop_delegate.state()); +} + +TEST_F(MenuButtonTest, + InkDropStateForMenuButtonActivationsWithListenerThatDoesntAcquireALock) { + TestMenuButtonListener menu_button_listener; + CreateMenuButtonWithMenuButtonListener(&menu_button_listener); + TestInkDropDelegate ink_drop_delegate; + button()->set_ink_drop_delegate(&ink_drop_delegate); + button()->Activate(nullptr); + + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, ink_drop_delegate.state()); +} + +TEST_F( + MenuButtonTest, + InkDropStateForMenuButtonActivationsWithListenerThatDontReleaseAllLocks) { + PressStateMenuButtonListener menu_button_listener(false); + CreateMenuButtonWithMenuButtonListener(&menu_button_listener); + menu_button_listener.set_menu_button(button()); + TestInkDropDelegate ink_drop_delegate; + button()->set_ink_drop_delegate(&ink_drop_delegate); + button()->Activate(nullptr); + + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop_delegate.state()); + + // Prevent the button from accessing invalid memory during clean up. + button()->set_ink_drop_delegate(nullptr); +} + +TEST_F(MenuButtonTest, + InkDropStateForMenuButtonActivationsWithListenerThatReleaseAllLocks) { + PressStateMenuButtonListener menu_button_listener(true); + CreateMenuButtonWithMenuButtonListener(&menu_button_listener); + menu_button_listener.set_menu_button(button()); + TestInkDropDelegate ink_drop_delegate; + button()->set_ink_drop_delegate(&ink_drop_delegate); + button()->Activate(nullptr); + + EXPECT_EQ(InkDropState::DEACTIVATED, ink_drop_delegate.state()); +} + +TEST_F(MenuButtonTest, InkDropStateForMenuButtonsWithPressedLocks) { + CreateMenuButtonWithNoListener(); + TestInkDropDelegate ink_drop_delegate; + button()->set_ink_drop_delegate(&ink_drop_delegate); + + scoped_ptr<MenuButton::PressedLock> pressed_lock1( + new MenuButton::PressedLock(button())); + + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop_delegate.state()); + + scoped_ptr<MenuButton::PressedLock> pressed_lock2( + new MenuButton::PressedLock(button())); + + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop_delegate.state()); + + pressed_lock1.reset(); + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop_delegate.state()); + + pressed_lock2.reset(); + EXPECT_EQ(InkDropState::DEACTIVATED, ink_drop_delegate.state()); +} + +// Verifies only one ink drop animation is triggered when multiple PressedLocks +// are attached to a MenuButton. +TEST_F(MenuButtonTest, OneInkDropAnimationForReentrantPressedLocks) { + CreateMenuButtonWithNoListener(); + TestInkDropDelegate ink_drop_delegate; + button()->set_ink_drop_delegate(&ink_drop_delegate); + + scoped_ptr<MenuButton::PressedLock> pressed_lock1( + new MenuButton::PressedLock(button())); + + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop_delegate.state()); + ink_drop_delegate.OnAction(InkDropState::ACTION_PENDING); + + scoped_ptr<MenuButton::PressedLock> pressed_lock2( + new MenuButton::PressedLock(button())); + + EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop_delegate.state()); +} + +// Verifies the InkDropState is left as ACTIVATED if a PressedLock is active +// before another Activation occurs. +TEST_F(MenuButtonTest, + InkDropStateForMenuButtonWithPressedLockBeforeActivation) { + TestMenuButtonListener menu_button_listener; + CreateMenuButtonWithMenuButtonListener(&menu_button_listener); + TestInkDropDelegate ink_drop_delegate; + button()->set_ink_drop_delegate(&ink_drop_delegate); + MenuButton::PressedLock lock(button()); + + button()->Activate(nullptr); + + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop_delegate.state()); +} + #if defined(USE_AURA) // Tests that the MenuButton does not become pressed if it can be dragged, and a @@ -411,9 +548,7 @@ TEST_F(MenuButtonTest, DraggableMenuButtonDoesNotActivateOnDrag) { SetDragDropClient(GetContext(), &drag_client); button()->PrependPreTargetHandler(&drag_client); - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - generator.set_current_location(gfx::Point(10, 10)); - generator.DragMouseBy(10, 0); + generator()->DragMouseBy(10, 0); EXPECT_EQ(nullptr, menu_button_listener.last_source()); EXPECT_EQ(Button::STATE_NORMAL, menu_button_listener.last_source_state()); } @@ -424,44 +559,17 @@ TEST_F(MenuButtonTest, DraggableMenuButtonDoesNotActivateOnDrag) { #if !defined(OS_MACOSX) || defined(USE_AURA) // Tests if the listener is notified correctly when a gesture tap happens on a -// MenuButton that has a regular ButtonListener. -TEST_F(MenuButtonTest, ActivateNonDropDownOnGestureTap) { - TestButtonListener button_listener; - CreateMenuButtonWithButtonListener(&button_listener); - - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - - // Move the mouse outside the menu button so that it doesn't impact the - // button state. - generator.MoveMouseTo(400, 400); - EXPECT_FALSE(button()->IsMouseHovered()); - - generator.GestureTapAt(gfx::Point(10, 10)); - - // Check that MenuButton has notified the listener on gesture tap event, while - // it was in hovered state. - EXPECT_EQ(button(), button_listener.last_sender()); - EXPECT_EQ(ui::ET_GESTURE_TAP, button_listener.last_event_type()); - EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_state()); - - // The button should go back to it's normal state since the gesture ended. - EXPECT_EQ(Button::STATE_NORMAL, button()->state()); -} - -// Tests if the listener is notified correctly when a gesture tap happens on a // MenuButton that has a MenuButtonListener. TEST_F(MenuButtonTest, ActivateDropDownOnGestureTap) { TestMenuButtonListener menu_button_listener; CreateMenuButtonWithMenuButtonListener(&menu_button_listener); - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - // Move the mouse outside the menu button so that it doesn't impact the // button state. - generator.MoveMouseTo(400, 400); + generator()->MoveMouseTo(400, 400); EXPECT_FALSE(button()->IsMouseHovered()); - generator.GestureTapAt(gfx::Point(10, 10)); + generator()->GestureTapAt(gfx::Point(10, 10)); // Check that MenuButton has notified the listener, while it was in pressed // state. @@ -477,12 +585,10 @@ TEST_F(MenuButtonTest, ActivateDropDownOnGestureTap) { TEST_F(MenuButtonTest, TouchFeedbackDuringTap) { TestMenuButtonListener menu_button_listener; CreateMenuButtonWithMenuButtonListener(&menu_button_listener); - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - generator.set_current_location(gfx::Point(10, 10)); - generator.PressTouch(); + generator()->PressTouch(); EXPECT_EQ(Button::STATE_HOVERED, button()->state()); - generator.ReleaseTouch(); + generator()->ReleaseTouch(); EXPECT_EQ(Button::STATE_HOVERED, menu_button_listener.last_source_state()); } @@ -491,13 +597,11 @@ TEST_F(MenuButtonTest, TouchFeedbackDuringTap) { TEST_F(MenuButtonTest, TouchFeedbackDuringTapCancel) { TestMenuButtonListener menu_button_listener; CreateMenuButtonWithMenuButtonListener(&menu_button_listener); - ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); - generator.set_current_location(gfx::Point(10, 10)); - generator.PressTouch(); + generator()->PressTouch(); EXPECT_EQ(Button::STATE_HOVERED, button()->state()); - generator.MoveTouch(gfx::Point(10, 30)); - generator.ReleaseTouch(); + generator()->MoveTouch(gfx::Point(10, 30)); + generator()->ReleaseTouch(); EXPECT_EQ(Button::STATE_NORMAL, button()->state()); EXPECT_EQ(nullptr, menu_button_listener.last_source()); } @@ -505,3 +609,4 @@ TEST_F(MenuButtonTest, TouchFeedbackDuringTapCancel) { #endif // !defined(OS_MACOSX) || defined(USE_AURA) } // namespace views +} // namespace test diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc index fa957ef61d4..3de636fbdc1 100644 --- a/chromium/ui/views/controls/combobox/combobox.cc +++ b/chromium/ui/views/controls/combobox/combobox.cc @@ -12,6 +12,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/base/default_style.h" #include "ui/base/ime/input_method.h" #include "ui/base/models/combobox_model.h" #include "ui/base/models/combobox_model_observer.h" @@ -26,6 +27,7 @@ #include "ui/native_theme/native_theme.h" #include "ui/native_theme/native_theme_aura.h" #include "ui/resources/grit/ui_resources.h" +#include "ui/views/background.h" #include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/combobox/combobox_listener.h" @@ -37,6 +39,7 @@ #include "ui/views/mouse_constants.h" #include "ui/views/painter.h" #include "ui/views/resources/grit/views_resources.h" +#include "ui/views/style/platform_style.h" #include "ui/views/widget/widget.h" namespace views { @@ -352,6 +355,9 @@ Combobox::Combobox(ui::ComboboxModel* model) ModelChanged(); SetFocusable(true); UpdateBorder(); + // set_background() takes ownership but takes a raw pointer. + scoped_ptr<Background> b = PlatformStyle::CreateComboboxBackground(); + set_background(b.release()); // Initialize the button images. Button::ButtonState button_states[] = { @@ -391,7 +397,7 @@ Combobox::~Combobox() { // static const gfx::FontList& Combobox::GetFontList() { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - return rb.GetFontList(ui::ResourceBundle::BaseFont); + return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); } void Combobox::SetStyle(Style style) { @@ -455,6 +461,12 @@ void Combobox::SetInvalid(bool invalid) { SchedulePaint(); } +int Combobox::GetArrowButtonWidth() const { + return GetDisclosureArrowLeftPadding() + + ArrowSize().width() + + GetDisclosureArrowRightPadding(); +} + void Combobox::Layout() { PrefixDelegate::Layout(); @@ -528,7 +540,7 @@ bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { return false; } - return menu_runner_; + return !!menu_runner_; } bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { @@ -688,7 +700,7 @@ void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { } void Combobox::UpdateBorder() { - scoped_ptr<FocusableBorder> border(new FocusableBorder()); + scoped_ptr<FocusableBorder> border(PlatformStyle::CreateComboboxBorder()); if (style_ == STYLE_ACTION) border->SetInsets(5, 10, 5, 10); if (invalid_) @@ -711,7 +723,8 @@ void Combobox::PaintText(gfx::Canvas* canvas) { int y = insets.top(); int text_height = height() - insets.height(); SkColor text_color = GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_LabelEnabledColor); + enabled() ? ui::NativeTheme::kColorId_LabelEnabledColor : + ui::NativeTheme::kColorId_LabelDisabledColor); DCHECK_GE(selected_index_, 0); DCHECK_LT(selected_index_, model()->GetItemCount()); @@ -739,19 +752,9 @@ void Combobox::PaintText(gfx::Canvas* canvas) { arrow_size.height()); AdjustBoundsForRTLUI(&arrow_bounds); - // TODO(estade): hack alert! Remove this direct call into CommonTheme. For now - // STYLE_ACTION isn't properly themed so we have to override the NativeTheme - // behavior. See crbug.com/384071 - if (style_ == STYLE_ACTION) { - ui::CommonThemePaintComboboxArrow(canvas->sk_canvas(), arrow_bounds); - } else { - ui::NativeTheme::ExtraParams ignored; - GetNativeTheme()->Paint(canvas->sk_canvas(), - ui::NativeTheme::kComboboxArrow, - ui::NativeTheme::kNormal, - arrow_bounds, - ignored); - } + gfx::ImageSkia arrow_image = PlatformStyle::CreateComboboxArrow( + enabled(), style_); + canvas->DrawImageInt(arrow_image, arrow_bounds.x(), arrow_bounds.y()); } void Combobox::PaintButtons(gfx::Canvas* canvas) { @@ -892,21 +895,7 @@ int Combobox::GetDisclosureArrowRightPadding() const { } gfx::Size Combobox::ArrowSize() const { -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - // TODO(estade): hack alert! This should always use GetNativeTheme(). For now - // STYLE_ACTION isn't properly themed so we have to override the NativeTheme - // behavior. See crbug.com/384071 - const ui::NativeTheme* native_theme_for_arrow = - style_ == STYLE_ACTION ? ui::NativeThemeAura::instance() - : GetNativeTheme(); -#else - const ui::NativeTheme* native_theme_for_arrow = GetNativeTheme(); -#endif - - ui::NativeTheme::ExtraParams ignored; - return native_theme_for_arrow->GetPartSize(ui::NativeTheme::kComboboxArrow, - ui::NativeTheme::kNormal, - ignored); + return PlatformStyle::CreateComboboxArrow(enabled(), style_).size(); } gfx::Size Combobox::GetContentSize() const { diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h index a0f5b48b161..64e4baa9be1 100644 --- a/chromium/ui/views/controls/combobox/combobox.h +++ b/chromium/ui/views/controls/combobox/combobox.h @@ -85,6 +85,10 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate, public ButtonListener { void SetInvalid(bool invalid); bool invalid() const { return invalid_; } + // Returns the width of the arrow button component of the combobox: the arrow + // button itself, and the padding on either side of it. + int GetArrowButtonWidth() const; + // Overridden from View: gfx::Size GetPreferredSize() const override; const char* GetClassName() const override; diff --git a/chromium/ui/views/controls/focusable_border.cc b/chromium/ui/views/controls/focusable_border.cc index f92a3009f1e..b3ddd149272 100644 --- a/chromium/ui/views/controls/focusable_border.cc +++ b/chromium/ui/views/controls/focusable_border.cc @@ -42,14 +42,8 @@ void FocusableBorder::Paint(const View& view, gfx::Canvas* canvas) { path.addRect(gfx::RectToSkRect(view.GetLocalBounds()), SkPath::kCW_Direction); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); - SkColor color = override_color_; - if (use_default_color_) { - color = view.GetNativeTheme()->GetSystemColor( - view.HasFocus() ? ui::NativeTheme::kColorId_FocusedBorderColor : - ui::NativeTheme::kColorId_UnfocusedBorderColor); - } - - paint.setColor(color); + + paint.setColor(GetCurrentColor(view)); paint.setStrokeWidth(SkIntToScalar(2)); canvas->DrawPath(path, paint); @@ -67,4 +61,12 @@ void FocusableBorder::SetInsets(int top, int left, int bottom, int right) { insets_.Set(top, left, bottom, right); } +SkColor FocusableBorder::GetCurrentColor(const View& view) const { + if (!use_default_color_) + return override_color_; + return view.GetNativeTheme()->GetSystemColor( + view.HasFocus() ? ui::NativeTheme::kColorId_FocusedBorderColor : + ui::NativeTheme::kColorId_UnfocusedBorderColor); +} + } // namespace views diff --git a/chromium/ui/views/controls/focusable_border.h b/chromium/ui/views/controls/focusable_border.h index eb24ca54de3..862049ab185 100644 --- a/chromium/ui/views/controls/focusable_border.h +++ b/chromium/ui/views/controls/focusable_border.h @@ -36,6 +36,9 @@ class VIEWS_EXPORT FocusableBorder : public Border { gfx::Insets GetInsets() const override; gfx::Size GetMinimumSize() const override; + protected: + SkColor GetCurrentColor(const View& view) const; + private: gfx::Insets insets_; diff --git a/chromium/ui/views/controls/focusable_rounded_border_mac.cc b/chromium/ui/views/controls/focusable_rounded_border_mac.cc new file mode 100644 index 00000000000..ae4522a7c8b --- /dev/null +++ b/chromium/ui/views/controls/focusable_rounded_border_mac.cc @@ -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. + +#include "ui/views/controls/focusable_rounded_border_mac.h" + +#include "ui/gfx/canvas.h" +#include "ui/native_theme/native_theme_mac.h" + +namespace { + +const int kThickness = 1; + +} // namespace + +namespace views { + +FocusableRoundedBorder::FocusableRoundedBorder() { + // TODO(ellyjones): These insets seem like they shouldn't be big enough, but + // they are, and insetting by corner_radius_ instead produces gargantuan + // padding. Why is that? + SetInsets(kThickness, kThickness, kThickness, kThickness); +} + +FocusableRoundedBorder::~FocusableRoundedBorder() {} + +// For now, this is similar to RoundedRectBorder::Paint(), but this method will +// likely diverge in future. +// TODO(ellyjones): Diverge it by adding soft focus rings. +void FocusableRoundedBorder::Paint(const View& view, gfx::Canvas* canvas) { + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(kThickness); + paint.setColor(GetCurrentColor(view)); + paint.setAntiAlias(true); + + float half_thickness = kThickness / 2.0f; + gfx::RectF bounds(view.GetLocalBounds()); + bounds.Inset(half_thickness, half_thickness); + canvas->DrawRoundRect(bounds, ui::NativeThemeMac::kComboboxCornerRadius, + paint); +} + +} // namespace views diff --git a/chromium/ui/views/controls/focusable_rounded_border_mac.h b/chromium/ui/views/controls/focusable_rounded_border_mac.h new file mode 100644 index 00000000000..1821cd414da --- /dev/null +++ b/chromium/ui/views/controls/focusable_rounded_border_mac.h @@ -0,0 +1,30 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_FOCUSABLE_ROUNDED_BORDER_H_ +#define UI_VIEWS_CONTROLS_FOCUSABLE_ROUNDED_BORDER_H_ + +#include "base/macros.h" +#include "ui/views/controls/focusable_border.h" +#include "ui/views/view.h" + +namespace views { + +// A Border that changes colors in accordance with the system color scheme when +// the View it borders is focused. +class FocusableRoundedBorder : public FocusableBorder { + public: + FocusableRoundedBorder(); + ~FocusableRoundedBorder() override; + + // Border: + void Paint(const View& view, gfx::Canvas* canvas) override; + + private: + DISALLOW_COPY_AND_ASSIGN(FocusableRoundedBorder); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_FOCUSABLE_ROUNDED_BORDER_H_ diff --git a/chromium/ui/views/controls/glow_hover_controller.cc b/chromium/ui/views/controls/glow_hover_controller.cc index 9a5fa0bc4ce..ef08308f7e7 100644 --- a/chromium/ui/views/controls/glow_hover_controller.cc +++ b/chromium/ui/views/controls/glow_hover_controller.cc @@ -9,8 +9,8 @@ namespace views { // Amount to scale the opacity. -static const double kTrackOpacityScale = 0.25; -static const double kHighlightOpacityScale = 1.0; +static const double kSubtleOpacityScale = 0.45; +static const double kPronouncedOpacityScale = 1.0; // How long the hover state takes. static const int kTrackHoverDurationMs = 400; @@ -18,7 +18,7 @@ static const int kTrackHoverDurationMs = 400; GlowHoverController::GlowHoverController(views::View* view) : view_(view), animation_(this), - opacity_scale_(kTrackOpacityScale) { + opacity_scale_(kSubtleOpacityScale) { animation_.set_delegate(this); } @@ -39,13 +39,13 @@ void GlowHoverController::SetLocation(const gfx::Point& location) { void GlowHoverController::Show(Style style) { switch (style) { case SUBTLE: - opacity_scale_ = kTrackOpacityScale; + opacity_scale_ = kSubtleOpacityScale; animation_.SetSlideDuration(kTrackHoverDurationMs); animation_.SetTweenType(gfx::Tween::EASE_OUT); animation_.Show(); break; case PRONOUNCED: - opacity_scale_ = kHighlightOpacityScale; + opacity_scale_ = kPronouncedOpacityScale; // Force the end state to show immediately. animation_.Show(); animation_.End(); @@ -69,8 +69,8 @@ double GlowHoverController::GetAnimationValue() const { } SkAlpha GlowHoverController::GetAlpha() const { - return static_cast<SkAlpha>(gfx::ToFlooredInt( - 0.5 + animation_.CurrentValueBetween(0., 255 * opacity_scale_))); + return static_cast<SkAlpha>(animation_.CurrentValueBetween( + 0, gfx::ToRoundedInt(255 * opacity_scale_))); } bool GlowHoverController::ShouldDraw() const { diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc index 7bda1d17da8..7127ca4f070 100644 --- a/chromium/ui/views/controls/image_view.cc +++ b/chromium/ui/views/controls/image_view.cc @@ -65,7 +65,7 @@ void ImageView::SetImage(const gfx::ImageSkia* image_skia) { } } -const gfx::ImageSkia& ImageView::GetImage() { +const gfx::ImageSkia& ImageView::GetImage() const { return image_; } diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h index 401289a76cc..b3b762bcd43 100644 --- a/chromium/ui/views/controls/image_view.h +++ b/chromium/ui/views/controls/image_view.h @@ -50,9 +50,9 @@ class VIEWS_EXPORT ImageView : public View { // image. void SetImage(const gfx::ImageSkia* image_skia); - // Returns the image currently displayed or NULL of none is currently set. + // Returns the image currently displayed, which can be empty if not set. // The returned image is still owned by the ImageView. - const gfx::ImageSkia& GetImage(); + const gfx::ImageSkia& GetImage() const; // Set the desired image size for the receiving ImageView. void SetImageSize(const gfx::Size& image_size); diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc index 99137e6eb94..d1a6cafa9c1 100644 --- a/chromium/ui/views/controls/label.cc +++ b/chromium/ui/views/controls/label.cc @@ -18,6 +18,8 @@ #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/base/default_style.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/geometry/insets.h" @@ -25,17 +27,23 @@ #include "ui/native_theme/native_theme.h" namespace views { +namespace { + +const gfx::FontList& GetDefaultFontList() { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); +} + +} // namespace // static const char Label::kViewClassName[] = "Label"; const int Label::kFocusBorderPadding = 1; -Label::Label() { - Init(base::string16(), gfx::FontList()); +Label::Label() : Label(base::string16()) { } -Label::Label(const base::string16& text) { - Init(text, gfx::FontList()); +Label::Label(const base::string16& text) : Label(text, GetDefaultFontList()) { } Label::Label(const base::string16& text, const gfx::FontList& font_list) { @@ -431,6 +439,7 @@ void Label::MaybeBuildRenderTextLines() { rect.Inset(kFocusBorderPadding, kFocusBorderPadding); if (rect.IsEmpty()) return; + rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); gfx::HorizontalAlignment alignment = horizontal_alignment(); gfx::DirectionalityMode directionality = render_text_->directionality_mode(); diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc index 9d0743341ea..63a12a85c7f 100644 --- a/chromium/ui/views/controls/label_unittest.cc +++ b/chromium/ui/views/controls/label_unittest.cc @@ -322,7 +322,7 @@ TEST_F(LabelTest, TextChangeWithoutLayout) { TEST_F(LabelTest, EmptyLabelSizing) { Label label; - const gfx::Size expected_size(0, gfx::FontList().GetHeight()); + const gfx::Size expected_size(0, label.font_list().GetHeight()); EXPECT_EQ(expected_size, label.GetPreferredSize()); label.SetMultiLine(!label.multi_line()); EXPECT_EQ(expected_size, label.GetPreferredSize()); diff --git a/chromium/ui/views/controls/link.cc b/chromium/ui/views/controls/link.cc index adbe647aaf4..ec955b38597 100644 --- a/chromium/ui/views/controls/link.cc +++ b/chromium/ui/views/controls/link.cc @@ -10,7 +10,7 @@ #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_view_state.h" #include "ui/base/cursor/cursor.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" diff --git a/chromium/ui/views/controls/menu/menu_config.cc b/chromium/ui/views/controls/menu/menu_config.cc index 10b301dcbee..720a69aa707 100644 --- a/chromium/ui/views/controls/menu/menu_config.cc +++ b/chromium/ui/views/controls/menu/menu_config.cc @@ -29,6 +29,7 @@ MenuConfig::MenuConfig() separator_upper_height(3), separator_lower_height(4), separator_spacing_height(3), + separator_thickness(1), show_mnemonics(false), scroll_arrow_height(3), label_to_minor_text_padding(10), diff --git a/chromium/ui/views/controls/menu/menu_config.h b/chromium/ui/views/controls/menu/menu_config.h index 156a86b4d66..a4ecc47839b 100644 --- a/chromium/ui/views/controls/menu/menu_config.h +++ b/chromium/ui/views/controls/menu/menu_config.h @@ -77,6 +77,9 @@ struct VIEWS_EXPORT MenuConfig { // Height of a ui::SPACING_SEPARATOR. int separator_spacing_height; + // Thickness of the drawn separator line in pixels. + int separator_thickness; + // Are mnemonics shown? bool show_mnemonics; diff --git a/chromium/ui/views/controls/menu/menu_config_android.cc b/chromium/ui/views/controls/menu/menu_config_android.cc deleted file mode 100644 index 9f5c73f7add..00000000000 --- a/chromium/ui/views/controls/menu/menu_config_android.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/menu/menu_config.h" - -namespace views { - -void MenuConfig::Init() { - // Android uses the config provided by data member initializers. -} - -} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_config_mac.mm b/chromium/ui/views/controls/menu/menu_config_mac.mm index 18c6fe2dbe4..2e00665c377 100644 --- a/chromium/ui/views/controls/menu/menu_config_mac.mm +++ b/chromium/ui/views/controls/menu/menu_config_mac.mm @@ -6,8 +6,18 @@ #import <AppKit/AppKit.h> +#include "base/mac/mac_util.h" + namespace views { +namespace { + +// The height of the menu separator in pixels. +const int kMenuSeparatorHeight = 2; +const int kMenuSeparatorHeightMavericks = 1; + +} // namespace + void MenuConfig::Init() { font_list = gfx::FontList(gfx::Font([NSFont menuFontOfSize:0.0])); menu_vertical_border_size = 4; @@ -21,6 +31,9 @@ void MenuConfig::Init() { separator_height = 13; separator_upper_height = 7; separator_lower_height = 6; + separator_thickness = base::mac::IsOSMavericks() + ? kMenuSeparatorHeightMavericks + : kMenuSeparatorHeight; align_arrow_and_shortcut = true; icons_in_label = true; check_selected_combobox_item = true; diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index 10b2cd914e7..fcc41e8168a 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -45,20 +45,14 @@ #include "ui/views/win/hwnd_util.h" #endif +#if defined(USE_AURA) +#include "ui/views/controls/menu/menu_key_event_handler.h" +#endif + using base::Time; using base::TimeDelta; using ui::OSExchangeData; -// Period of the scroll timer (in milliseconds). -static const int kScrollTimerMS = 30; - -// Amount of time from when the drop exits the menu and the menu is hidden. -static const int kCloseOnExitTime = 1200; - -// If a context menu is invoked by touch, we shift the menu by this offset so -// that the finger does not obscure the menu. -static const int kCenteredContextMenuYOffset = -15; - namespace views { namespace { @@ -66,7 +60,17 @@ namespace { // When showing context menu on mouse down, the user might accidentally select // the menu item on the subsequent mouse up. To prevent this, we add the // following delay before the user is able to select an item. -static int menu_selection_hold_time_ms = kMinimumMsPressedToActivate; +int menu_selection_hold_time_ms = kMinimumMsPressedToActivate; + +// Period of the scroll timer (in milliseconds). +const int kScrollTimerMS = 30; + +// Amount of time from when the drop exits the menu and the menu is hidden. +const int kCloseOnExitTime = 1200; + +// If a context menu is invoked by touch, we shift the menu by this offset so +// that the finger does not obscure the menu. +const int kCenteredContextMenuYOffset = -15; // The spacing offset for the bubble tip. const int kBubbleTipSizeLeftRight = 12; @@ -93,14 +97,12 @@ bool TitleMatchesMnemonic(MenuItemView* menu, base::char16 key) { } // Returns the first descendant of |view| that is hot tracked. -static CustomButton* GetFirstHotTrackedView(View* view) { +CustomButton* GetFirstHotTrackedView(View* view) { if (!view) return NULL; CustomButton* button = CustomButton::AsCustomButton(view); - if (button) { - if (button->IsHotTracked()) - return button; - } + if (button && button->IsHotTracked()) + return button; for (int i = 0; i < view->child_count(); ++i) { CustomButton* hot_view = GetFirstHotTrackedView(view->child_at(i)); @@ -115,7 +117,7 @@ static CustomButton* GetFirstHotTrackedView(View* view) { // the first view (if |forward| is false, iterating starts at the last view). If // |forward| is true the children are considered first to last, otherwise last // to first. -static View* GetFirstFocusableView(View* view, int start, bool forward) { +View* GetFirstFocusableView(View* view, int start, bool forward) { if (forward) { for (int i = start == -1 ? 0 : start; i < view->child_count(); ++i) { View* deepest = GetFirstFocusableView(view->child_at(i), -1, forward); @@ -133,15 +135,13 @@ static View* GetFirstFocusableView(View* view, int start, bool forward) { } // Returns the first child of |start| that is focusable. -static View* GetInitialFocusableView(View* start, bool forward) { +View* GetInitialFocusableView(View* start, bool forward) { return GetFirstFocusableView(start, -1, forward); } // Returns the next view after |start_at| that is focusable. Returns NULL if // there are no focusable children of |ancestor| after |start_at|. -static View* GetNextFocusableView(View* ancestor, - View* start_at, - bool forward) { +View* GetNextFocusableView(View* ancestor, View* start_at, bool forward) { DCHECK(ancestor->Contains(start_at)); View* parent = start_at; do { @@ -158,6 +158,101 @@ static View* GetNextFocusableView(View* ancestor, return NULL; } +#if defined(OS_WIN) || defined(OS_CHROMEOS) +// Determines the correct cooridinates and window to repost |event| to, if it is +// a mouse or touch event. +static void RepostEventImpl(const ui::LocatedEvent* event, + const gfx::Point& screen_loc, + gfx::NativeView native_view, + gfx::NativeWindow window) { + if (!event->IsMouseEvent() && !event->IsTouchEvent()) { + // TODO(rbyers): Gesture event repost is tricky to get right + // crbug.com/170987. + DCHECK(event->IsGestureEvent()); + return; + } + + if (!native_view) + return; + +#if defined(OS_WIN) + gfx::Point screen_loc_pixels = gfx::win::DIPToScreenPoint(screen_loc); + HWND target_window = ::WindowFromPoint(screen_loc_pixels.ToPOINT()); + // If we don't find a native window for the HWND at the current location, + // then attempt to find a native window from its parent if one exists. + // There are HWNDs created outside views, which don't have associated + // native windows. + if (!window) { + HWND parent = ::GetParent(target_window); + if (parent) { + aura::WindowTreeHost* host = + aura::WindowTreeHost::GetForAcceleratedWidget(parent); + if (host) { + target_window = parent; + window = host->window(); + } + } + } + // Convert screen_loc to pixels for the Win32 API's like WindowFromPoint, + // PostMessage/SendMessage to work correctly. These API's expect the + // coordinates to be in pixels. + if (event->IsMouseEvent()) { + HWND source_window = HWNDForNativeView(native_view); + if (!target_window || !source_window || + GetWindowThreadProcessId(source_window, NULL) != + GetWindowThreadProcessId(target_window, NULL)) { + // Even though we have mouse capture, windows generates a mouse event if + // the other window is in a separate thread. Only repost an event if + // |target_window| and |source_window| were created on the same thread, + // else double events can occur and lead to bad behavior. + return; + } + + // Determine whether the click was in the client area or not. + // NOTE: WM_NCHITTEST coordinates are relative to the screen. + LPARAM coords = MAKELPARAM(screen_loc_pixels.x(), screen_loc_pixels.y()); + LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords); + const bool client_area = nc_hit_result == HTCLIENT; + + // TODO(sky): this isn't right. The event to generate should correspond with + // the event we just got. MouseEvent only tells us what is down, which may + // differ. Need to add ability to get changed button from MouseEvent. + int event_type; + int flags = event->flags(); + if (flags & ui::EF_LEFT_MOUSE_BUTTON) { + event_type = client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN; + } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) { + event_type = client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN; + } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) { + event_type = client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN; + } else { + NOTREACHED(); + return; + } + + int window_x = screen_loc_pixels.x(); + int window_y = screen_loc_pixels.y(); + if (client_area) { + POINT pt = {window_x, window_y}; + ScreenToClient(target_window, &pt); + window_x = pt.x; + window_y = pt.y; + } + + WPARAM target = client_area ? event->native_event().wParam : nc_hit_result; + LPARAM window_coords = MAKELPARAM(window_x, window_y); + PostMessage(target_window, event_type, target, window_coords); + return; + } +#endif + // Non Aura window. + if (!window) + return; + + MenuMessageLoop::RepostEventToWindow(event, window, screen_loc); +} +#endif // defined(OS_WIN) || defined(OS_CHROMEOS) + } // namespace // MenuScrollTask -------------------------------------------------------------- @@ -268,12 +363,15 @@ struct MenuController::SelectByCharDetails { // MenuController:State ------------------------------------------------------ MenuController::State::State() - : item(NULL), + : item(nullptr), + hot_button(nullptr), submenu_open(false), anchor(MENU_ANCHOR_TOPLEFT), context_menu(false) { } +MenuController::State::State(const State& other) = default; + MenuController::State::~State() {} // MenuController ------------------------------------------------------------ @@ -327,6 +425,8 @@ MenuItemView* MenuController::Run(Widget* parent, // blocking/non-blocking shouldn't be needed. DCHECK(blocking_run_); + state_.hot_button = hot_button_; + hot_button_ = nullptr; // We're already showing, push the current state. menu_stack_.push_back( std::make_pair(state_, make_linked_ptr(pressed_lock_.release()))); @@ -335,6 +435,12 @@ MenuItemView* MenuController::Run(Widget* parent, DCHECK_EQ(owner_, parent); } else { showing_ = true; + +#if defined(USE_AURA) + // Only create a MenuKeyEventHandler for non-nested menus. Nested menus will + // use the existing one. + key_event_handler_.reset(new MenuKeyEventHandler); +#endif } // Reset current state. @@ -450,9 +556,14 @@ bool MenuController::OnMousePressed(SubmenuView* source, if (forward_to_root) { ui::MouseEvent event_for_root(event); + // Reset hot-tracking if a different view is getting a mouse press. ConvertLocatedEventForRootView(source, forward_to_root, &event_for_root); View* view = forward_to_root->GetEventHandlerForPoint(event_for_root.location()); + CustomButton* button = CustomButton::AsCustomButton(view); + if (hot_button_ != button) + SetHotTrackedButton(button); + // Empty menu items are always handled by the menu controller. if (!view || view->id() != MenuItemView::kEmptyMenuItemViewID) { bool processed = forward_to_root->ProcessMousePressed(event_for_root); @@ -610,8 +721,21 @@ void MenuController::OnMouseMoved(SubmenuView* source, } MenuHostRootView* root_view = GetRootView(source, event.location()); - if (root_view) + if (root_view) { root_view->ProcessMouseMoved(event); + + // Update hot-tracked button when a button state is changed with a mouse + // event. It is necessary to track it for accurate hot-tracking when both + // mouse and keyboard are used to navigate the menu. + ui::MouseEvent event_for_root(event); + ConvertLocatedEventForRootView(source, root_view, &event_for_root); + View* view = + root_view->GetEventHandlerForPoint(event_for_root.location()); + CustomButton* button = CustomButton::AsCustomButton(view); + if (button && button->IsHotTracked()) + SetHotTrackedButton(button); + } + HandleMouseLocation(source, event.location()); } @@ -629,6 +753,18 @@ bool MenuController::OnMouseWheel(SubmenuView* source, void MenuController::OnGestureEvent(SubmenuView* source, ui::GestureEvent* event) { + MenuHostRootView* root_view = GetRootView(source, event->location()); + if (root_view) { + // Reset hot-tracking if a different view is getting a touch event. + ui::GestureEvent event_for_root(*event); + ConvertLocatedEventForRootView(source, root_view, &event_for_root); + View* view = + root_view->GetEventHandlerForPoint(event_for_root.location()); + CustomButton* button = CustomButton::AsCustomButton(view); + if (hot_button_ && hot_button_ != button) + SetHotTrackedButton(nullptr); + } + MenuPart part = GetMenuPart(source, event->location()); if (event->type() == ui::ET_GESTURE_TAP_DOWN) { SetSelectionOnPointerDown(source, event); @@ -646,8 +782,8 @@ void MenuController::OnGestureEvent(SubmenuView* source, !(part.menu->HasSubmenu())) { if (part.menu->GetDelegate()->IsTriggerableEvent( part.menu, *event)) { - Accept(part.menu, event->flags()); item_selected_by_touch_ = true; + Accept(part.menu, event->flags()); } event->StopPropagation(); } else if (part.type == MenuPart::MENU_ITEM) { @@ -695,10 +831,22 @@ View* MenuController::GetTooltipHandlerForPoint(SubmenuView* source, void MenuController::ViewHierarchyChanged( SubmenuView* source, const View::ViewHierarchyChangedDetails& details) { - // If the current mouse handler is removed, remove it as the handler. - if (!details.is_add && details.child == current_mouse_event_target_) { - current_mouse_event_target_ = nullptr; - current_mouse_pressed_state_ = 0; + if (!details.is_add) { + // If the current mouse handler is removed, remove it as the handler. + if (details.child == current_mouse_event_target_) { + current_mouse_event_target_ = nullptr; + current_mouse_pressed_state_ = 0; + } + // Update |hot_button_| (both in |this| and in |menu_stack_| if it gets + // removed while a menu is up. + if (details.child == hot_button_) { + hot_button_ = nullptr; + for (auto nested_state : menu_stack_) { + State& state = nested_state.first; + if (details.child == state.hot_button) + state.hot_button = nullptr; + } + } } } @@ -889,7 +1037,7 @@ ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent( void MenuController::UpdateSubmenuSelection(SubmenuView* submenu) { if (submenu->IsShowing()) { - gfx::Point point = GetScreen()->GetCursorScreenPoint(); + gfx::Point point = gfx::Screen::GetScreen()->GetCursorScreenPoint(); const SubmenuView* root_submenu = submenu->GetMenuItem()->GetRootMenuItem()->GetSubmenu(); View::ConvertPointFromScreen( @@ -926,11 +1074,8 @@ void MenuController::SetSelection(MenuItemView* menu_item, size_t new_size = new_path.size(); bool pending_item_changed = pending_state_.item != menu_item; - if (pending_item_changed && pending_state_.item) { - CustomButton* button = GetFirstHotTrackedView(pending_state_.item); - if (button) - button->SetHotTracked(false); - } + if (pending_item_changed && pending_state_.item) + SetHotTrackedButton(nullptr); // Notify the old path it isn't selected. MenuDelegate* current_delegate = @@ -973,7 +1118,7 @@ void MenuController::SetSelection(MenuItemView* menu_item, (MenuDepth(menu_item) != 1 || menu_item->GetType() != MenuItemView::SUBMENU)) { menu_item->NotifyAccessibilityEvent( - ui::AX_EVENT_FOCUS, true); + ui::AX_EVENT_SELECTION, true); } } @@ -1059,7 +1204,9 @@ void MenuController::StartDrag(SubmenuView* source, } void MenuController::OnKeyDown(ui::KeyboardCode key_code) { - DCHECK(blocking_run_); + // Do not process while performing drag-and-drop + if (!blocking_run_) + return; switch (key_code) { case ui::VKEY_UP: @@ -1149,6 +1296,15 @@ void MenuController::OnKeyDown(ui::KeyboardCode key_code) { break; } +#if defined(OS_WIN) + // On Windows, pressing Alt and F10 keys should hide the menu to match the + // OS behavior. + case ui::VKEY_MENU: + case ui::VKEY_F10: + Cancel(EXIT_ALL); + break; +#endif + default: break; } @@ -1172,6 +1328,7 @@ MenuController::MenuController(bool blocking, last_drop_operation_(MenuDelegate::DROP_UNKNOWN), showing_submenu_(false), active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()), + hot_button_(nullptr), delegate_(delegate), message_loop_depth_(0), closing_event_time_(base::TimeDelta()), @@ -1208,7 +1365,7 @@ bool MenuController::SendAcceleratorToHotTrackedView() { ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE); hot_view->AcceleratorPressed(accelerator); CustomButton* button = static_cast<CustomButton*>(hot_view); - button->SetHotTracked(true); + SetHotTrackedButton(button); return true; } @@ -1235,14 +1392,16 @@ void MenuController::UpdateInitialLocation(const gfx::Rect& bounds, // Calculate the bounds of the monitor we'll show menus on. Do this once to // avoid repeated system queries for the info. - pending_state_.monitor_bounds = GetScreen()->GetDisplayNearestPoint( - bounds.origin()).work_area(); + pending_state_.monitor_bounds = gfx::Screen::GetScreen() + ->GetDisplayNearestPoint(bounds.origin()) + .work_area(); if (!pending_state_.monitor_bounds.Contains(bounds)) { // Use the monitor area if the work area doesn't contain the bounds. This // handles showing a menu from the launcher. - gfx::Rect monitor_area = GetScreen()->GetDisplayNearestPoint( - bounds.origin()).bounds(); + gfx::Rect monitor_area = gfx::Screen::GetScreen() + ->GetDisplayNearestPoint(bounds.origin()) + .bounds(); if (monitor_area.Contains(bounds)) pending_state_.monitor_bounds = monitor_area; } @@ -1275,7 +1434,8 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source, return false; } - gfx::NativeWindow window_under_mouse = GetScreen()->GetWindowUnderCursor(); + gfx::NativeWindow window_under_mouse = + gfx::Screen::GetScreen()->GetWindowUnderCursor(); // TODO(oshima): Replace with views only API. if (!owner_ || window_under_mouse != owner_->GetNativeWindow()) return false; @@ -1303,7 +1463,8 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source, // There is a sibling menu, update the button state, hide the current menu // and show the new one. - pressed_lock_.reset(new MenuButton::PressedLock(button)); + pressed_lock_.reset( + new MenuButton::PressedLock(button, true /* is_sibling_menu_show */)); // Need to reset capture when we show the menu again, otherwise we aren't // going to get any events. @@ -1967,8 +2128,7 @@ void MenuController::IncrementSelection( // select the first menu item that is visible and enabled. if (item->GetSubmenu()->GetMenuItemCount()) { MenuItemView* to_select = FindInitialSelectableMenuItem(item, direction); - if (to_select) - SetSelection(to_select, SELECTION_DEFAULT); + SetInitialHotTrackedView(to_select, direction); return; } } @@ -1976,22 +2136,17 @@ void MenuController::IncrementSelection( if (item->has_children()) { CustomButton* button = GetFirstHotTrackedView(item); if (button) { - button->SetHotTracked(false); - View* to_make_hot = GetNextFocusableView( - item, button, direction == INCREMENT_SELECTION_DOWN); - CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot); - if (button_hot) { - button_hot->SetHotTracked(true); - return; - } - } else { - View* to_make_hot = - GetInitialFocusableView(item, direction == INCREMENT_SELECTION_DOWN); - CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot); - if (button_hot) { - button_hot->SetHotTracked(true); - return; - } + DCHECK_EQ(hot_button_, button); + SetHotTrackedButton(nullptr); + } + bool direction_is_down = direction == INCREMENT_SELECTION_DOWN; + View* to_make_hot = button + ? GetNextFocusableView(item, button, direction_is_down) + : GetInitialFocusableView(item, direction_is_down); + CustomButton* hot_button = CustomButton::AsCustomButton(to_make_hot); + if (hot_button) { + SetHotTrackedButton(hot_button); + return; } } @@ -2003,14 +2158,7 @@ void MenuController::IncrementSelection( if (parent->GetSubmenu()->GetMenuItemAt(i) == item) { MenuItemView* to_select = FindNextSelectableMenuItem(parent, i, direction); - if (!to_select) - break; - SetSelection(to_select, SELECTION_DEFAULT); - View* to_make_hot = GetInitialFocusableView( - to_select, direction == INCREMENT_SELECTION_DOWN); - CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot); - if (button_hot) - button_hot->SetHotTracked(true); + SetInitialHotTrackedView(to_select, direction); break; } } @@ -2127,6 +2275,9 @@ void MenuController::AcceptOrSelect(MenuItemView* parent, } void MenuController::SelectByChar(base::char16 character) { + // Do not process while performing drag-and-drop + if (!blocking_run_) + return; if (!character) return; @@ -2159,127 +2310,46 @@ void MenuController::SelectByChar(base::char16 character) { } } -void MenuController::RepostEvent(SubmenuView* source, - const ui::LocatedEvent* event, - const gfx::Point& screen_loc, - gfx::NativeView native_view, - gfx::NativeWindow window) { - if (!event->IsMouseEvent() && !event->IsTouchEvent()) { - // TODO(rbyers): Gesture event repost is tricky to get right - // crbug.com/170987. - DCHECK(event->IsGestureEvent()); - return; - } - -#if defined(OS_WIN) - if (!state_.item) { - // We some times get an event after closing all the menus. Ignore it. Make - // sure the menu is in fact not visible. If the menu is visible, then - // we're in a bad state where we think the menu isn't visibile but it is. - DCHECK(!source->GetWidget()->IsVisible()); - return; - } - - state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); -#endif - - if (!native_view) - return; - -#if defined(OS_WIN) - gfx::Point screen_loc_pixels = gfx::win::DIPToScreenPoint(screen_loc); - HWND target_window = ::WindowFromPoint(screen_loc_pixels.ToPOINT()); - // If we don't find a native window for the HWND at the current location, - // then attempt to find a native window from its parent if one exists. - // There are HWNDs created outside views, which don't have associated - // native windows. - if (!window) { - HWND parent = ::GetParent(target_window); - if (parent) { - aura::WindowTreeHost* host = - aura::WindowTreeHost::GetForAcceleratedWidget(parent); - if (host) { - target_window = parent; - window = host->window(); - } - } - } - // Convert screen_loc to pixels for the Win32 API's like WindowFromPoint, - // PostMessage/SendMessage to work correctly. These API's expect the - // coordinates to be in pixels. - if (event->IsMouseEvent()) { - HWND source_window = HWNDForNativeView(native_view); - if (!target_window || !source_window || - GetWindowThreadProcessId(source_window, NULL) != - GetWindowThreadProcessId(target_window, NULL)) { - // Even though we have mouse capture, windows generates a mouse event if - // the other window is in a separate thread. Only repost an event if - // |target_window| and |source_window| were created on the same thread, - // else double events can occur and lead to bad behavior. - return; - } - - // Determine whether the click was in the client area or not. - // NOTE: WM_NCHITTEST coordinates are relative to the screen. - LPARAM coords = MAKELPARAM(screen_loc_pixels.x(), screen_loc_pixels.y()); - LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords); - const bool client_area = nc_hit_result == HTCLIENT; - - // TODO(sky): this isn't right. The event to generate should correspond with - // the event we just got. MouseEvent only tells us what is down, which may - // differ. Need to add ability to get changed button from MouseEvent. - int event_type; - int flags = event->flags(); - if (flags & ui::EF_LEFT_MOUSE_BUTTON) { - event_type = client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN; - } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) { - event_type = client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN; - } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) { - event_type = client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN; - } else { - NOTREACHED(); - return; - } - - int window_x = screen_loc_pixels.x(); - int window_y = screen_loc_pixels.y(); - if (client_area) { - POINT pt = { window_x, window_y }; - ScreenToClient(target_window, &pt); - window_x = pt.x; - window_y = pt.y; - } - - WPARAM target = client_area ? event->native_event().wParam : nc_hit_result; - LPARAM window_coords = MAKELPARAM(window_x, window_y); - PostMessage(target_window, event_type, target, window_coords); - return; - } -#endif - // Non Aura window. - if (!window) - return; - - MenuMessageLoop::RepostEventToWindow(event, window, screen_loc); -} - void MenuController::RepostEventAndCancel(SubmenuView* source, const ui::LocatedEvent* event) { // Cancel can lead to the deletion |source| so we save the view and window to // be used when reposting the event. gfx::Point screen_loc(event->location()); View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc); + +#if defined(OS_WIN) || defined(OS_CHROMEOS) gfx::NativeView native_view = source->GetWidget()->GetNativeView(); gfx::NativeWindow window = nullptr; if (native_view) { - gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view); + gfx::Screen* screen = gfx::Screen::GetScreen(); window = screen->GetWindowAtScreenPoint(screen_loc); } +#endif #if defined(OS_WIN) - // We're going to close and we own the event capture. We need to repost the - // event, otherwise the window the user clicked on won't get the event. - RepostEvent(source, event, screen_loc, native_view, window); + if (event->IsMouseEvent() || event->IsTouchEvent()) { + bool async_run = async_run_; + if (state_.item) { + state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); + RepostEventImpl(event, screen_loc, native_view, window); + } else { + // We some times get an event after closing all the menus. Ignore it. Make + // sure the menu is in fact not visible. If the menu is visible, then + // we're in a bad state where we think the menu isn't visibile but it is. + DCHECK(!source->GetWidget()->IsVisible()); + } + + // We're going to close and we own the event capture. We need to repost the + // event, otherwise the window the user clicked on won't get the event. + // RepostEvent(source, event, screen_loc, native_view, window); + // MenuController may have been deleted if |async_run_| so check for an + // active + // instance before accessing member variables. + if (!GetActiveInstance()) { + DCHECK(async_run); + return; + } + } #endif // Determine target to see if a complete or partial close of the menu should @@ -2300,7 +2370,7 @@ void MenuController::RepostEventAndCancel(SubmenuView* source, // is handled normally after the context menu has exited. We call // RepostEvent after Cancel so that event capture has been released so // that finding the event target is unaffected by the current capture. - RepostEvent(source, event, screen_loc, native_view, window); + RepostEventImpl(event, screen_loc, native_view, window); #endif } @@ -2427,8 +2497,7 @@ View* MenuController::GetActiveMouseView() { void MenuController::SetExitType(ExitType type) { exit_type_ = type; // Exit nested message loops as soon as possible. We do this as - // MessagePumpDispatcher is only invoked before native events, which means - // its entirely possible for a Widget::CloseNow() task to be processed before + // it's entirely possible for a Widget::CloseNow() task to be processed before // the next native message. We quite the nested message loop as soon as // possible to avoid having deleted views classes (such as widgets and // rootviews) on the stack when the nested message loop stops. @@ -2453,7 +2522,8 @@ void MenuController::ExitAsyncRun() { MenuItemView* result = ExitMenuRun(); delegate->OnMenuClosed(internal::MenuControllerDelegate::NOTIFY_DELEGATE, result, accept_event_flags_); - if (nested && exit_type_ == EXIT_ALL) + // MenuController may have been deleted by |delegate|. + if (GetActiveInstance() && nested && exit_type_ == EXIT_ALL) ExitAsyncRun(); } @@ -2494,6 +2564,7 @@ MenuItemView* MenuController::ExitMenuRun() { // The menus are already showing, so we don't have to show them. state_ = menu_stack_.back().first; pending_state_ = menu_stack_.back().first; + hot_button_ = state_.hot_button; nested_pressed_lock = menu_stack_.back().second; menu_stack_.pop_back(); // Even though the menus are nested, there may not be nested delegates. @@ -2503,6 +2574,10 @@ MenuItemView* MenuController::ExitMenuRun() { async_run_ = delegate_stack_.back().second; } } else { +#if defined(USE_AURA) + key_event_handler_.reset(); +#endif + showing_ = false; did_capture_ = false; } @@ -2529,9 +2604,11 @@ MenuItemView* MenuController::ExitMenuRun() { } } - // Reset our pressed lock to the previous state's, if there was one. - // The lock handles the case if the button was destroyed. + // Reset our pressed lock and hot-tracked state to the previous state's, if + // they were active. The lock handles the case if the button was destroyed. pressed_lock_.reset(nested_pressed_lock.release()); + if (hot_button_) + hot_button_->SetHotTracked(true); return result; } @@ -2568,10 +2645,29 @@ void MenuController::HandleMouseLocation(SubmenuView* source, } } -gfx::Screen* MenuController::GetScreen() { - Widget* root = owner_ ? owner_->GetTopLevelWidget() : NULL; - return root ? gfx::Screen::GetScreenFor(root->GetNativeView()) - : gfx::Screen::GetNativeScreen(); +void MenuController::SetInitialHotTrackedView( + MenuItemView* item, + SelectionIncrementDirectionType direction) { + if (!item) + return; + SetSelection(item, SELECTION_DEFAULT); + View* hot_view = + GetInitialFocusableView(item, direction == INCREMENT_SELECTION_DOWN); + SetHotTrackedButton(CustomButton::AsCustomButton(hot_view)); +} + +void MenuController::SetHotTrackedButton(CustomButton* hot_button) { + if (hot_button == hot_button_) { + // Hot-tracked state may change outside of the MenuController. Correct it. + if (hot_button && !hot_button->IsHotTracked()) + hot_button->SetHotTracked(true); + return; + } + if (hot_button_) + hot_button_->SetHotTracked(false); + hot_button_ = hot_button; + if (hot_button) + hot_button->SetHotTracked(true); } } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h index 12af0aa1874..dc21826d6d4 100644 --- a/chromium/ui/views/controls/menu/menu_controller.h +++ b/chromium/ui/views/controls/menu/menu_controller.h @@ -26,9 +26,6 @@ #include "ui/views/controls/menu/menu_delegate.h" #include "ui/views/widget/widget_observer.h" -namespace base { -class MessagePumpDispatcher; -} namespace gfx { class Screen; } @@ -46,10 +43,12 @@ class MouseEvent; class SubmenuView; class View; +#if defined(USE_AURA) +class MenuKeyEventHandler; +#endif + namespace internal { class MenuControllerDelegate; -class MenuEventDispatcher; -class MenuMessagePumpDispatcher; class MenuRunnerImpl; } @@ -199,8 +198,6 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { static void TurnOffMenuSelectionHoldForTest(); private: - friend class internal::MenuEventDispatcher; - friend class internal::MenuMessagePumpDispatcher; friend class internal::MenuRunnerImpl; friend class test::MenuControllerTest; friend class MenuKeyEventHandler; @@ -240,11 +237,16 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { // Tracks selection information. struct State { State(); + State(const State& other); ~State(); // The selected menu item. MenuItemView* item; + // 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; + // If item has a submenu this indicates if the submenu is showing. bool submenu_open; @@ -497,20 +499,11 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { // the title. void SelectByChar(base::char16 key); - // For Windows and Aura we repost an event for some events that dismiss - // the context menu. The event is then reprocessed to cause its result - // if the context menu had not been present. - // On non-aura Windows, a new mouse event is generated and posted to - // the window (if there is one) at the location of the event. On - // aura, the event is reposted on the RootWindow. - void RepostEvent(SubmenuView* source, - const ui::LocatedEvent* event, - const gfx::Point& screen_loc, - gfx::NativeView native_view, - gfx::NativeWindow window); - // For Windows and Aura we repost an event which dismisses the |source| menu. - // The menu is also canceled dependent on the target of the event. + // The menu may also be canceled depending on the target of the event. |event| + // is then processed without the menu present. On non-aura Windows, a new + // mouse event is generated and posted to the window (if there is one) at the + // location of the event. On aura, the event is reposted on the RootWindow. void RepostEventAndCancel(SubmenuView* source, const ui::LocatedEvent* event); // Sets the drop target to new_item. @@ -565,8 +558,12 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { void HandleMouseLocation(SubmenuView* source, const gfx::Point& mouse_location); - // Retrieve an appropriate Screen. - gfx::Screen* GetScreen(); + // Sets hot-tracked state to the first focusable descendant view of |item|. + void SetInitialHotTrackedView(MenuItemView* item, + SelectionIncrementDirectionType direction); + + // Updates the current |hot_button_| and its hot tracked state. + void SetHotTrackedButton(CustomButton* hot_button); // The active instance. static MenuController* active_instance_; @@ -666,6 +663,9 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { // See UpdateActiveMouseView() for details. const int active_mouse_view_id_; + // Current hot tracked child button if any. + CustomButton* hot_button_; + internal::MenuControllerDelegate* delegate_; // How deep we are in nested message loops. This should be at most 2 (when @@ -682,7 +682,7 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { // screen coordinates). Otherwise this will be (0, 0). gfx::Point menu_start_mouse_press_loc_; - // Controls behviour differences between an asynchronous run, and other types + // Controls behaviour differences between an asynchronous run, and other types // of run (blocking, drag and drop). bool async_run_; @@ -704,6 +704,10 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { scoped_ptr<MenuMessageLoop> message_loop_; +#if defined(USE_AURA) + scoped_ptr<MenuKeyEventHandler> key_event_handler_; +#endif + DISALLOW_COPY_AND_ASSIGN(MenuController); }; diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc index e9074f0704e..ee26fb24200 100644 --- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -4,6 +4,7 @@ #include "ui/views/controls/menu/menu_controller.h" +#include "base/callback.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -21,16 +22,14 @@ #include "ui/views/controls/menu/menu_delegate.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_message_loop.h" +#include "ui/views/controls/menu/menu_scroll_view_container.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/test/views_test_base.h" -#if defined(OS_WIN) -#include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" -#endif - #if defined(USE_AURA) #include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window.h" +#include "ui/views/controls/menu/menu_key_event_handler.h" #endif #if defined(USE_X11) @@ -92,6 +91,11 @@ class TestMenuControllerDelegate : public internal::MenuControllerDelegate { return on_menu_closed_mouse_event_flags_; } + // On a subsequent call to OnMenuClosed |controller| will be deleted. + void set_on_menu_closed_callback(const base::Closure& callback) { + on_menu_closed_callback_ = callback; + } + // internal::MenuControllerDelegate: void OnMenuClosed(NotifyType type, MenuItemView* menu, @@ -107,6 +111,9 @@ class TestMenuControllerDelegate : public internal::MenuControllerDelegate { MenuItemView* on_menu_closed_menu_; int on_menu_closed_mouse_event_flags_; + // Optional callback triggered during OnMenuClosed + base::Closure on_menu_closed_callback_; + DISALLOW_COPY_AND_ASSIGN(TestMenuControllerDelegate); }; @@ -114,7 +121,8 @@ TestMenuControllerDelegate::TestMenuControllerDelegate() : on_menu_closed_called_(0), on_menu_closed_notify_type_(NOTIFY_DELEGATE), on_menu_closed_menu_(nullptr), - on_menu_closed_mouse_event_flags_(0) {} + on_menu_closed_mouse_event_flags_(0), + on_menu_closed_callback_() {} void TestMenuControllerDelegate::OnMenuClosed(NotifyType type, MenuItemView* menu, @@ -123,6 +131,8 @@ void TestMenuControllerDelegate::OnMenuClosed(NotifyType type, on_menu_closed_notify_type_ = type; on_menu_closed_menu_ = menu; on_menu_closed_mouse_event_flags_ = mouse_event_flags; + if (!on_menu_closed_callback_.is_null()) + on_menu_closed_callback_.Run(); } void TestMenuControllerDelegate::SiblingMenuCreated(MenuItemView* menu) {} @@ -240,12 +250,7 @@ class MenuControllerTest : public ViewsTestBase { void TearDown() override { owner_->CloseNow(); - - menu_controller_->showing_ = false; - menu_controller_->owner_ = nullptr; - delete menu_controller_; - menu_controller_ = nullptr; - + DestroyMenuController(); ViewsTestBase::TearDown(); } @@ -347,6 +352,13 @@ class MenuControllerTest : public ViewsTestBase { MenuController::INCREMENT_SELECTION_UP); } + void DestroyMenuControllerOnMenuClosed(TestMenuControllerDelegate* delegate) { + // Unretained() is safe here as the test should outlive the delegate. If not + // we want to know. + delegate->set_on_menu_closed_callback(base::Bind( + &MenuControllerTest::DestroyMenuController, base::Unretained(this))); + } + MenuItemView* FindInitialSelectableMenuItemDown(MenuItemView* parent) { return menu_controller_->FindInitialSelectableMenuItem( parent, MenuController::INCREMENT_SELECTION_DOWN); @@ -396,7 +408,17 @@ class MenuControllerTest : public ViewsTestBase { menu_controller_->SetSelectionOnPointerDown(source, event); } + // Note that coordinates of events passed to MenuController must be in that of + // the MenuScrollViewContainer. + void ProcessMouseMoved(SubmenuView* source, const ui::MouseEvent& event) { + menu_controller_->OnMouseMoved(source, event); + } + void RunMenu() { +#if defined(USE_AURA) + scoped_ptr<MenuKeyEventHandler> key_event_handler(new MenuKeyEventHandler); +#endif + menu_controller_->message_loop_depth_++; menu_controller_->RunMessageLoop(false); menu_controller_->message_loop_depth_--; @@ -426,7 +448,46 @@ class MenuControllerTest : public ViewsTestBase { return menu_controller_->exit_type_; } + void AddButtonMenuItems() { + menu_item()->SetBounds(0, 0, 200, 300); + MenuItemView* item_view = + menu_item()->AppendMenuItemWithLabel(5, base::ASCIIToUTF16("Five")); + for (int i = 0; i < 3; ++i) { + LabelButton* button = + new LabelButton(nullptr, base::ASCIIToUTF16("Label")); + button->SetFocusable(true); + item_view->AddChildView(button); + } + menu_item()->GetSubmenu()->ShowAt(owner(), menu_item()->bounds(), false); + } + + CustomButton* GetHotButton() { + return menu_controller_->hot_button_; + } + + void SetHotTrackedButton(CustomButton* hot_button) { + menu_controller_->SetHotTrackedButton(hot_button); + } + + void ExitMenuRun() { + menu_controller_->SetExitType(MenuController::ExitType::EXIT_OUTERMOST); + menu_controller_->ExitMenuRun(); + } + private: + void DestroyMenuController() { + if (!menu_controller_) + return; + + if (!owner_->IsClosed()) + owner_->RemoveObserver(menu_controller_); + + menu_controller_->showing_ = false; + menu_controller_->owner_ = nullptr; + delete menu_controller_; + menu_controller_ = nullptr; + } + void Init() { owner_.reset(new Widget); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); @@ -436,14 +497,7 @@ class MenuControllerTest : public ViewsTestBase { new ui::test::EventGenerator(GetContext(), owner_->GetNativeWindow())); owner_->Show(); -#if defined(OS_WIN) - dispatcher_client_.reset(new DesktopDispatcherClient); - aura::client::SetDispatcherClient(owner_->GetNativeView()->GetRootWindow(), - dispatcher_client_.get()); -#endif - SetupMenuItem(); - SetupMenuController(); } @@ -467,10 +521,6 @@ class MenuControllerTest : public ViewsTestBase { menu_item_->SetController(menu_controller_); } -#if defined(OS_WIN) - scoped_ptr<aura::client::DispatcherClient> dispatcher_client_; -#endif - scoped_ptr<Widget> owner_; scoped_ptr<ui::test::EventGenerator> event_generator_; scoped_ptr<TestMenuItemViewShown> menu_item_; @@ -685,6 +735,176 @@ TEST_F(MenuControllerTest, SelectByChar) { ResetSelection(); } +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)); + ASSERT_NE(nullptr, button1); + CustomButton* button2 = + CustomButton::AsCustomButton(buttons_view->child_at(1)); + ASSERT_NE(nullptr, button2); + CustomButton* button3 = + CustomButton::AsCustomButton(buttons_view->child_at(2)); + ASSERT_NE(nullptr, button2); + + // Handle searching for 'f'; should find "Four". + SelectByChar('f'); + EXPECT_EQ(4, pending_state_item()->GetCommand()); + + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_FALSE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + + // Move selection to |button1|. + IncrementSelection(); + EXPECT_EQ(5, pending_state_item()->GetCommand()); + EXPECT_TRUE(button1->IsHotTracked()); + EXPECT_FALSE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + + // Move selection to |button2|. + IncrementSelection(); + EXPECT_EQ(5, pending_state_item()->GetCommand()); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_TRUE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + + // Move selection to |button3|. + IncrementSelection(); + EXPECT_EQ(5, pending_state_item()->GetCommand()); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_FALSE(button2->IsHotTracked()); + EXPECT_TRUE(button3->IsHotTracked()); + + // Move a mouse to hot track the |button1|. + SubmenuView* sub_menu = menu_item()->GetSubmenu(); + gfx::Point location(button1->GetBoundsInScreen().CenterPoint()); + View::ConvertPointFromScreen(sub_menu->GetScrollViewContainer(), &location); + ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location, + ui::EventTimeForNow(), 0, 0); + ProcessMouseMoved(sub_menu, event); + + // Incrementing selection should move hot tracking to the second button (next + // after the first button). + IncrementSelection(); + EXPECT_EQ(5, pending_state_item()->GetCommand()); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_TRUE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + + // Increment selection twice to wrap around. + IncrementSelection(); + IncrementSelection(); + EXPECT_EQ(1, pending_state_item()->GetCommand()); + + // Clear references in menu controller to the menu item that is going away. + ResetSelection(); +} + +TEST_F(MenuControllerTest, DeleteChildButtonView) { + AddButtonMenuItems(); + + // Handle searching for 'f'; should find "Four". + SelectByChar('f'); + EXPECT_EQ(4, pending_state_item()->GetCommand()); + + View* buttons_view = menu_item()->GetSubmenu()->child_at(4); + ASSERT_NE(nullptr, buttons_view); + CustomButton* button1 = + CustomButton::AsCustomButton(buttons_view->child_at(0)); + ASSERT_NE(nullptr, button1); + CustomButton* button2 = + CustomButton::AsCustomButton(buttons_view->child_at(1)); + ASSERT_NE(nullptr, button2); + CustomButton* button3 = + CustomButton::AsCustomButton(buttons_view->child_at(2)); + ASSERT_NE(nullptr, button2); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_FALSE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + + // Increment twice to move selection to |button2|. + IncrementSelection(); + IncrementSelection(); + EXPECT_EQ(5, pending_state_item()->GetCommand()); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_TRUE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + + // Delete |button2| while it is hot-tracked. + // This should update MenuController via ViewHierarchyChanged and reset + // |hot_button_|. + delete button2; + + // Incrementing selection should now set hot-tracked item to |button1|. + // It should not crash. + IncrementSelection(); + EXPECT_EQ(5, pending_state_item()->GetCommand()); + EXPECT_TRUE(button1->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); +} + +// Creates a menu with CustomButton child views, simulates running a nested +// menu and tests that existing the nested run restores hot-tracked child view. +TEST_F(MenuControllerTest, ChildButtonHotTrackedWhenNested) { + AddButtonMenuItems(); + + // Handle searching for 'f'; should find "Four". + SelectByChar('f'); + EXPECT_EQ(4, pending_state_item()->GetCommand()); + + View* buttons_view = menu_item()->GetSubmenu()->child_at(4); + ASSERT_NE(nullptr, buttons_view); + CustomButton* button1 = + CustomButton::AsCustomButton(buttons_view->child_at(0)); + ASSERT_NE(nullptr, button1); + CustomButton* button2 = + CustomButton::AsCustomButton(buttons_view->child_at(1)); + ASSERT_NE(nullptr, button2); + CustomButton* button3 = + CustomButton::AsCustomButton(buttons_view->child_at(2)); + ASSERT_NE(nullptr, button2); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_FALSE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + + // Increment twice to move selection to |button2|. + IncrementSelection(); + IncrementSelection(); + EXPECT_EQ(5, pending_state_item()->GetCommand()); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_TRUE(button2->IsHotTracked()); + EXPECT_FALSE(button3->IsHotTracked()); + EXPECT_EQ(button2, GetHotButton()); + + MenuController* controller = menu_controller(); + controller->SetAsyncRun(true); + int mouse_event_flags = 0; + MenuItemView* run_result = + controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), + MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); + EXPECT_EQ(run_result, nullptr); + + // |button2| should stay in hot-tracked state but menu controller should not + // track it anymore (preventing resetting hot-tracked state when changing + // selection while a nested run is active). + EXPECT_TRUE(button2->IsHotTracked()); + EXPECT_EQ(nullptr, GetHotButton()); + + // Setting hot-tracked button while nested should get reverted when nested + // menu run ends. + SetHotTrackedButton(button1); + EXPECT_TRUE(button1->IsHotTracked()); + EXPECT_EQ(button1, GetHotButton()); + + ExitMenuRun(); + EXPECT_FALSE(button1->IsHotTracked()); + EXPECT_TRUE(button2->IsHotTracked()); + EXPECT_EQ(button2, GetHotButton()); +} + // Tests that a menu opened asynchronously, will notify its // MenuControllerDelegate when Accept is called. TEST_F(MenuControllerTest, AsynchronousAccept) { @@ -859,7 +1079,7 @@ TEST_F(MenuControllerTest, AsynchronousRepostEvent) { false, false, &mouse_event_flags); EXPECT_EQ(run_result, nullptr); - // Show a sub menu to targert with a pointer selection. However have the event + // Show a sub menu to target with a pointer selection. However have the event // occur outside of the bounds of the entire menu. SubmenuView* sub_menu = item->GetSubmenu(); sub_menu->ShowAt(owner(), item->bounds(), false); @@ -890,7 +1110,7 @@ TEST_F(MenuControllerTest, AsynchronousTouchEventRepostEvent) { TestMenuControllerDelegate* delegate = menu_controller_delegate(); controller->SetAsyncRun(true); - // Show a sub menu to targert with a touch event. However have the event occur + // Show a sub menu to target with a touch event. However have the event occur // outside of the bounds of the entire menu. MenuItemView* item = menu_item(); SubmenuView* sub_menu = item->GetSubmenu(); @@ -936,5 +1156,87 @@ TEST_F(MenuControllerTest, AsynchronousNestedExitOutermost) { RunMenu(); } +// Tests that having the MenuController deleted during RepostEvent does not +// cause a crash. ASAN bots should not detect use-after-free in MenuController. +TEST_F(MenuControllerTest, AsynchronousRepostEventDeletesController) { + MenuController* controller = menu_controller(); + scoped_ptr<TestMenuControllerDelegate> nested_delegate( + new TestMenuControllerDelegate()); + + ASSERT_FALSE(IsAsyncRun()); + + controller->AddNestedDelegate(nested_delegate.get()); + controller->SetAsyncRun(true); + + EXPECT_TRUE(IsAsyncRun()); + EXPECT_EQ(nested_delegate.get(), GetCurrentDelegate()); + + MenuItemView* item = menu_item(); + int mouse_event_flags = 0; + MenuItemView* run_result = + controller->Run(owner(), nullptr, item, gfx::Rect(), MENU_ANCHOR_TOPLEFT, + false, false, &mouse_event_flags); + EXPECT_EQ(run_result, nullptr); + + // Show a sub menu to target with a pointer selection. However have the event + // occur outside of the bounds of the entire menu. + SubmenuView* sub_menu = item->GetSubmenu(); + sub_menu->ShowAt(owner(), item->bounds(), true); + gfx::Point location(sub_menu->bounds().bottom_right()); + location.Offset(1, 1); + ui::MouseEvent event(ui::ET_MOUSE_PRESSED, location, location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); + + // This will lead to MenuController being deleted during the event repost. + // The remainder of this test, and TearDown should not crash. + DestroyMenuControllerOnMenuClosed(nested_delegate.get()); + // When attempting to select outside of all menus this should lead to a + // shutdown. This should not crash while attempting to repost the event. + SetSelectionOnPointerDown(sub_menu, &event); + + // Close to remove observers before test TearDown + sub_menu->Close(); + EXPECT_EQ(1, nested_delegate->on_menu_closed_called()); +} + +// Tests that having the MenuController deleted during OnGestureEvent does not +// cause a crash. ASAN bots should not detect use-after-free in MenuController. +TEST_F(MenuControllerTest, AsynchronousGestureDeletesController) { + MenuController* controller = menu_controller(); + scoped_ptr<TestMenuControllerDelegate> nested_delegate( + new TestMenuControllerDelegate()); + ASSERT_FALSE(IsAsyncRun()); + + controller->AddNestedDelegate(nested_delegate.get()); + controller->SetAsyncRun(true); + + EXPECT_TRUE(IsAsyncRun()); + EXPECT_EQ(nested_delegate.get(), GetCurrentDelegate()); + + MenuItemView* item = menu_item(); + int mouse_event_flags = 0; + MenuItemView* run_result = + controller->Run(owner(), nullptr, item, gfx::Rect(), MENU_ANCHOR_TOPLEFT, + false, false, &mouse_event_flags); + EXPECT_EQ(run_result, nullptr); + + // Show a sub menu to target with a tap event. + SubmenuView* sub_menu = item->GetSubmenu(); + sub_menu->ShowAt(owner(), gfx::Rect(0, 0, 100, 100), true); + + gfx::Point location(sub_menu->bounds().CenterPoint()); + ui::GestureEvent event(location.x(), location.y(), 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_TAP)); + + // This will lead to MenuController being deleted during the processing of the + // gesture event. The remainder of this test, and TearDown should not crash. + DestroyMenuControllerOnMenuClosed(nested_delegate.get()); + controller->OnGestureEvent(sub_menu, &event); + + // Close to remove observers before test TearDown + sub_menu->Close(); + EXPECT_EQ(1, nested_delegate->on_menu_closed_called()); +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_event_dispatcher.cc b/chromium/ui/views/controls/menu/menu_event_dispatcher.cc deleted file mode 100644 index 9b7c231b939..00000000000 --- a/chromium/ui/views/controls/menu/menu_event_dispatcher.cc +++ /dev/null @@ -1,89 +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/controls/menu/menu_event_dispatcher.h" - -#include "base/memory/scoped_ptr.h" -#include "ui/aura/window.h" -#include "ui/events/event_utils.h" -#include "ui/events/keycodes/keyboard_code_conversion.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/views/controls/menu/menu_controller.h" -#include "ui/views/widget/widget.h" - -namespace views { -namespace internal { - -MenuEventDispatcher::MenuEventDispatcher(MenuController* controller) - : menu_controller_(controller) {} - -MenuEventDispatcher::~MenuEventDispatcher() {} - -bool MenuEventDispatcher::CanDispatchEvent(const ui::PlatformEvent& event) { - return true; -} - -uint32_t MenuEventDispatcher::DispatchEvent(const ui::PlatformEvent& event) { - bool should_perform_default = true; - bool should_process_event = true; - - // Check if the event should be handled. - scoped_ptr<ui::Event> ui_event(ui::EventFromNative(event)); - if (ui_event && menu_controller_->owner()) { - aura::Window* menu_window = menu_controller_->owner()->GetNativeWindow(); - aura::Window* target_window = static_cast<aura::Window*>( - static_cast<ui::EventTarget*>(menu_window->GetRootWindow())-> - GetEventTargeter()->FindTargetForEvent(menu_window, - ui_event.get())); - // TODO(flackr): The event shouldn't be handled if target_window is not - // menu_window, however the event targeter does not properly target the - // open menu. For now, we allow targeters to prevent handling by the menu. - if (!target_window) - should_process_event = false; - } - - if (menu_controller_->exit_type() != MenuController::EXIT_ALL && - menu_controller_->exit_type() != MenuController::EXIT_DESTROYED && - ui_event && should_process_event) { - switch (ui_event->type()) { - case ui::ET_KEY_PRESSED: { - should_perform_default = false; - - ui::KeyEvent* key_event = static_cast<ui::KeyEvent*>(ui_event.get()); - menu_controller_->OnKeyDown(key_event->key_code()); - if (menu_controller_->exit_type() != MenuController::EXIT_NONE) - break; - - // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. - int flags = key_event->flags(); - if ((flags & (ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) == 0) { - char c = ui::DomCodeToUsLayoutCharacter(key_event->code(), flags); - menu_controller_->SelectByChar(c); - } - break; - } - case ui::ET_KEY_RELEASED: - should_perform_default = false; - break; - case ui::ET_TOUCH_RELEASED: - case ui::ET_TOUCH_CANCELLED: - // Don't allow the event copy to clear the native touch id - // mapping, or we'll lose the mapping before the initial event - // has finished being dispatched. - static_cast<ui::TouchEvent*>(ui_event.get()) - ->set_should_remove_native_touch_id_mapping(false); - break; - default: - break; - } - } - - menu_controller_->TerminateNestedMessageLoopIfNecessary(); - - return should_perform_default ? ui::POST_DISPATCH_PERFORM_DEFAULT - : ui::POST_DISPATCH_NONE; -} - -} // namespace internal -} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_event_dispatcher.h b/chromium/ui/views/controls/menu/menu_event_dispatcher.h deleted file mode 100644 index 6b9e8e38b04..00000000000 --- a/chromium/ui/views/controls/menu/menu_event_dispatcher.h +++ /dev/null @@ -1,39 +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_VIEWS_CONTROLS_MENU_MENU_EVENT_DISPATCHER_H_ -#define UI_VIEWS_CONTROLS_MENU_MENU_EVENT_DISPATCHER_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "ui/events/platform/platform_event_dispatcher.h" - -namespace views { - -class MenuController; - -namespace internal { - -// A message-pump dispatcher object used to dispatch events from the nested -// message-loop initiated by the MenuController. -class MenuEventDispatcher : public ui::PlatformEventDispatcher { - public: - explicit MenuEventDispatcher(MenuController* menu_controller); - ~MenuEventDispatcher() override; - - private: - // ui::PlatformEventDispatcher: - bool CanDispatchEvent(const ui::PlatformEvent& event) override; - uint32_t DispatchEvent(const ui::PlatformEvent& event) override; - - MenuController* menu_controller_; - - DISALLOW_COPY_AND_ASSIGN(MenuEventDispatcher); -}; - -} // namespace internal -} // namespace views - -#endif // UI_VIEWS_CONTROLS_MENU_MENU_EVENT_DISPATCHER_H_ diff --git a/chromium/ui/views/controls/menu/menu_image_util.cc b/chromium/ui/views/controls/menu/menu_image_util.cc index f6398865bc0..55f6e551c2f 100644 --- a/chromium/ui/views/controls/menu/menu_image_util.cc +++ b/chromium/ui/views/controls/menu/menu_image_util.cc @@ -4,11 +4,10 @@ #include "ui/views/controls/menu/menu_image_util.h" -#include "ui/base/resource/material_design/material_design_controller.h" -#include "ui/base/resource/resource_bundle.h" +#include "ui/base/material_design/material_design_controller.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icons_public.h" -#include "ui/views/resources/grit/views_resources.h" namespace views { @@ -17,10 +16,14 @@ gfx::ImageSkia GetMenuCheckImage(SkColor icon_color) { icon_color); } -gfx::ImageSkia GetRadioButtonImage(bool selected) { - int image_id = selected ? IDR_MENU_RADIO_SELECTED : IDR_MENU_RADIO_EMPTY; - return ui::ResourceBundle::GetSharedInstance().GetImageNamed(image_id). - AsImageSkia(); +gfx::ImageSkia GetRadioButtonImage(bool toggled, + bool hovered, + SkColor default_icon_color) { + gfx::VectorIconId id = toggled ? gfx::VectorIconId::MENU_RADIO_SELECTED + : gfx::VectorIconId::MENU_RADIO_EMPTY; + SkColor color = + toggled && !hovered ? gfx::kGoogleBlue500 : default_icon_color; + return gfx::CreateVectorIcon(id, kMenuCheckSize, color); } gfx::ImageSkia GetSubmenuArrowImage(SkColor icon_color) { diff --git a/chromium/ui/views/controls/menu/menu_image_util.h b/chromium/ui/views/controls/menu/menu_image_util.h index cd7a352f8ff..5097c4852f9 100644 --- a/chromium/ui/views/controls/menu/menu_image_util.h +++ b/chromium/ui/views/controls/menu/menu_image_util.h @@ -17,10 +17,13 @@ const int kSubmenuArrowSize = 8; // Returns the Menu Check box image (always checked). gfx::ImageSkia GetMenuCheckImage(SkColor icon_color); -// Return the RadioButton image for given state. -// It returns the "selected" image when |selected| is -// true, or the "unselected" image if false. -gfx::ImageSkia GetRadioButtonImage(bool selected); +// Return the RadioButton image for given state. |toggled| is true when +// the radio option is active, |hovered| describes the menu higlight/selection +// state, and |default_icon_color| is the base color that should be used for +// the icon (which may be ignored based on the other two flags). +gfx::ImageSkia GetRadioButtonImage(bool toggled, + bool hovered, + SkColor default_icon_color); // Returns the image for submenu arrow for current RTL setting. gfx::ImageSkia GetSubmenuArrowImage(SkColor icon_color); diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 5721126cb4c..8e3013b1197 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -12,8 +12,8 @@ #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_view_state.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/models/menu_model.h" -#include "ui/base/resource/material_design/material_design_controller.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/geometry/rect.h" @@ -547,7 +547,7 @@ void MenuItemView::Layout() { continue; int width = child->GetPreferredSize().width(); child->SetBounds(x - width, 0, width, height()); - x -= width - kChildXPadding; + x -= width + kChildXPadding; } // Position |icon_view|. const MenuConfig& config = MenuConfig::instance(); @@ -815,8 +815,8 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { AdjustBoundsForRTLUI(&check_bounds); canvas->DrawImageInt(check, check_bounds.x(), check_bounds.y()); } else if (type_ == RADIO) { - gfx::ImageSkia image = - GetRadioButtonImage(delegate->IsItemChecked(GetCommand())); + 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(), diff --git a/chromium/ui/views/controls/menu/menu_key_event_handler.cc b/chromium/ui/views/controls/menu/menu_key_event_handler.cc index fc110b5ab1b..af0566e2af9 100644 --- a/chromium/ui/views/controls/menu/menu_key_event_handler.cc +++ b/chromium/ui/views/controls/menu/menu_key_event_handler.cc @@ -5,7 +5,6 @@ #include "ui/views/controls/menu/menu_key_event_handler.h" #include "ui/aura/env.h" -#include "ui/events/keycodes/keyboard_code_conversion.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/views_delegate.h" @@ -45,16 +44,24 @@ void MenuKeyEventHandler::OnKeyEvent(ui::KeyEvent* event) { return; } + event->StopPropagation(); + if (event->type() == ui::ET_KEY_PRESSED) { menu_controller->OnKeyDown(event->key_code()); + // Menu controller might have been deleted. + if (!MenuController::GetActiveInstance()) + return; // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. For // example Ctrl+<T> is an accelerator, but <T> only is a mnemonic. const int flags = event->flags(); if (menu_controller->exit_type() == MenuController::EXIT_NONE && (flags & kKeyFlagsMask) == 0) { - char c = ui::DomCodeToUsLayoutCharacter(event->code(), flags); + base::char16 c = event->GetCharacter(); menu_controller->SelectByChar(c); + // Menu controller might have been deleted. + if (!MenuController::GetActiveInstance()) + return; } } @@ -66,8 +73,6 @@ void MenuKeyEventHandler::OnKeyEvent(ui::KeyEvent* event) { if (result == ViewsDelegate::ProcessMenuAcceleratorResult::CLOSE_MENU) menu_controller->CancelAll(); } - - event->StopPropagation(); } } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_loop_aura.cc b/chromium/ui/views/controls/menu/menu_message_loop_aura.cc index 25d531a2be2..f2653751716 100644 --- a/chromium/ui/views/controls/menu/menu_message_loop_aura.cc +++ b/chromium/ui/views/controls/menu/menu_message_loop_aura.cc @@ -15,19 +15,12 @@ #include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/scoped_event_dispatcher.h" #include "ui/views/controls/menu/menu_controller.h" +#include "ui/views/controls/menu/menu_key_event_handler.h" #include "ui/views/widget/widget.h" #include "ui/wm/public/activation_change_observer.h" #include "ui/wm/public/activation_client.h" -#include "ui/wm/public/dispatcher_client.h" #include "ui/wm/public/drag_drop_client.h" -#if defined(OS_WIN) -#include "ui/base/win/internal_constants.h" -#include "ui/views/controls/menu/menu_message_pump_dispatcher_win.h" -#include "ui/views/win/hwnd_util.h" -#else -#include "ui/views/controls/menu/menu_key_event_handler.h" -#endif using aura::client::ScreenPositionClient; @@ -139,44 +132,18 @@ void MenuMessageLoopAura::Run(MenuController* controller, base::AutoReset<base::Closure> reset_quit_closure(&message_loop_quit_, base::Closure()); -#if defined(OS_WIN) - internal::MenuMessagePumpDispatcher nested_dispatcher(controller); - if (root) { - scoped_ptr<ActivationChangeObserverImpl> observer; - if (!nested_menu) - observer.reset(new ActivationChangeObserverImpl(controller, root)); - aura::client::DispatcherRunLoop run_loop( - aura::client::GetDispatcherClient(root), &nested_dispatcher); - message_loop_quit_ = run_loop.QuitClosure(); - run_loop.Run(); - } else { - base::MessageLoop* loop = base::MessageLoop::current(); - base::MessageLoop::ScopedNestableTaskAllower allow(loop); - base::RunLoop run_loop(&nested_dispatcher); - message_loop_quit_ = run_loop.QuitClosure(); - run_loop.Run(); - } -#else scoped_ptr<ActivationChangeObserverImpl> observer; if (root) { if (!nested_menu) observer.reset(new ActivationChangeObserverImpl(controller, root)); } - scoped_ptr<MenuKeyEventHandler> menu_event_filter; - if (!nested_menu) { - // If this is a nested menu, then the MenuKeyEventHandler would have been - // created already in the top parent menu. So no need to recreate it here. - menu_event_filter.reset(new MenuKeyEventHandler); - } - base::MessageLoop* loop = base::MessageLoop::current(); base::MessageLoop::ScopedNestableTaskAllower allow(loop); base::RunLoop run_loop; message_loop_quit_ = run_loop.QuitClosure(); run_loop.Run(); -#endif // defined(OS_WIN) } void MenuMessageLoopAura::QuitNow() { diff --git a/chromium/ui/views/controls/menu/menu_message_loop_aura.h b/chromium/ui/views/controls/menu/menu_message_loop_aura.h index bdb8352249f..fce8858dd09 100644 --- a/chromium/ui/views/controls/menu/menu_message_loop_aura.h +++ b/chromium/ui/views/controls/menu/menu_message_loop_aura.h @@ -11,10 +11,6 @@ #include "base/memory/scoped_ptr.h" #include "ui/views/controls/menu/menu_message_loop.h" -namespace base { -class MessagePumpDispatcher; -} - namespace ui { class ScopedEventDispatcher; } diff --git a/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.cc b/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.cc deleted file mode 100644 index fedb10e85ef..00000000000 --- a/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.cc +++ /dev/null @@ -1,85 +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/controls/menu/menu_message_pump_dispatcher_win.h" - -#include <windowsx.h> - -#include "ui/events/event_utils.h" -#include "ui/events/keycodes/keyboard_code_conversion.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/views/controls/menu/menu_controller.h" -#include "ui/views/controls/menu/menu_item_view.h" - -namespace views { -namespace internal { - -MenuMessagePumpDispatcher::MenuMessagePumpDispatcher(MenuController* controller) - : menu_controller_(controller) {} - -MenuMessagePumpDispatcher::~MenuMessagePumpDispatcher() {} - -uint32_t MenuMessagePumpDispatcher::Dispatch(const MSG& msg) { - DCHECK(menu_controller_->IsBlockingRun()); - - bool should_perform_default = true; - if (menu_controller_->exit_type() != MenuController::EXIT_ALL && - menu_controller_->exit_type() != MenuController::EXIT_DESTROYED) { - // NOTE: we don't get WM_ACTIVATE or anything else interesting in here. - switch (msg.message) { - case WM_CONTEXTMENU: { - MenuItemView* item = menu_controller_->pending_state_.item; - if (item && item->GetRootMenuItem() != item) { - gfx::Point screen_loc(0, item->height()); - View::ConvertPointToScreen(item, &screen_loc); - ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; - if (GET_X_LPARAM(msg.lParam) == -1 && GET_Y_LPARAM(msg.lParam) == -1) - source_type = ui::MENU_SOURCE_KEYBOARD; - item->GetDelegate()->ShowContextMenu( - item, item->GetCommand(), screen_loc, source_type); - } - should_perform_default = false; - break; - } - - // NOTE: focus wasn't changed when the menu was shown. As such, don't - // dispatch key events otherwise the focused window will get the events. - case WM_KEYDOWN: { - menu_controller_->OnKeyDown(ui::KeyboardCodeFromNative(msg)); - TranslateMessage(&msg); - should_perform_default = false; - break; - } - case WM_CHAR: { - menu_controller_->SelectByChar(static_cast<base::char16>(msg.wParam)); - should_perform_default = false; - break; - } - case WM_KEYUP: - case WM_SYSKEYUP: - // We may have been shown on a system key, as such don't do anything - // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and - // close the menu. - should_perform_default = false; - break; - - case WM_CANCELMODE: - case WM_SYSKEYDOWN: - // Exit immediately on system keys. - menu_controller_->Cancel(MenuController::EXIT_ALL); - should_perform_default = false; - break; - - default: - break; - } - } - - menu_controller_->TerminateNestedMessageLoopIfNecessary(); - return should_perform_default ? POST_DISPATCH_PERFORM_DEFAULT - : POST_DISPATCH_NONE; -} - -} // namespace internal -} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.h b/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.h deleted file mode 100644 index 44a0aade790..00000000000 --- a/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.h +++ /dev/null @@ -1,38 +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_VIEWS_CONTROLS_MENU_MENU_MESSAGE_PUMP_DISPATCHER_WIN_H_ -#define UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_PUMP_DISPATCHER_WIN_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "base/message_loop/message_pump_dispatcher.h" - -namespace views { - -class MenuController; - -namespace internal { - -// A message-pump dispatcher object used to dispatch events from the nested -// message-loop initiated by the MenuController. -class MenuMessagePumpDispatcher : public base::MessagePumpDispatcher { - public: - explicit MenuMessagePumpDispatcher(MenuController* menu_controller); - ~MenuMessagePumpDispatcher() override; - - private: - // base::MessagePumpDispatcher: - uint32_t Dispatch(const base::NativeEvent& event) override; - - MenuController* menu_controller_; - - DISALLOW_COPY_AND_ASSIGN(MenuMessagePumpDispatcher); -}; - -} // namespace internal -} // namespace views - -#endif // UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_PUMP_DISPATCHER_WIN_H_ diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.cc b/chromium/ui/views/controls/menu/menu_model_adapter.cc index f18d86fd34a..b63f9866774 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter.cc @@ -13,9 +13,14 @@ namespace views { MenuModelAdapter::MenuModelAdapter(ui::MenuModel* menu_model) + : MenuModelAdapter(menu_model, base::Closure() /*null callback*/) {} + +MenuModelAdapter::MenuModelAdapter(ui::MenuModel* menu_model, + const base::Closure& on_menu_closed_callback) : menu_model_(menu_model), triggerable_event_flags_(ui::EF_LEFT_MOUSE_BUTTON | - ui::EF_RIGHT_MOUSE_BUTTON) { + ui::EF_RIGHT_MOUSE_BUTTON), + on_menu_closed_callback_(on_menu_closed_callback) { DCHECK(menu_model); } @@ -264,6 +269,12 @@ void MenuModelAdapter::WillHideMenu(MenuItemView* menu) { NOTREACHED(); } +void MenuModelAdapter::OnMenuClosed(MenuItemView* menu, + MenuRunner::RunResult result) { + if (!on_menu_closed_callback_.is_null()) + on_menu_closed_callback_.Run(); +} + // MenuModelAdapter, private: void MenuModelAdapter::BuildMenuImpl(MenuItemView* menu, ui::MenuModel* model) { diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.h b/chromium/ui/views/controls/menu/menu_model_adapter.h index ecc4f1e4c02..c9799daadb3 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter.h +++ b/chromium/ui/views/controls/menu/menu_model_adapter.h @@ -7,6 +7,7 @@ #include <map> +#include "base/callback.h" #include "base/macros.h" #include "ui/views/controls/menu/menu_delegate.h" @@ -24,6 +25,8 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate { // The caller retains ownership of the ui::MenuModel instance and // must ensure it exists for the lifetime of the adapter. explicit MenuModelAdapter(ui::MenuModel* menu_model); + MenuModelAdapter(ui::MenuModel* menu_model, + const base::Closure& on_menu_closed_callback); ~MenuModelAdapter() override; // Populate a MenuItemView menu with the ui::MenuModel items @@ -75,6 +78,7 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate { void SelectionChanged(MenuItemView* menu) override; void WillShowMenu(MenuItemView* menu) override; void WillHideMenu(MenuItemView* menu) override; + void OnMenuClosed(MenuItemView* menu, MenuRunner::RunResult result) override; private: // Implementation of BuildMenu(). @@ -91,6 +95,9 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate { // Map MenuItems to MenuModels. Used to implement WillShowMenu(). std::map<MenuItemView*, ui::MenuModel*> menu_map_; + // Optional callback triggered during OnMenuClosed(). + base::Closure on_menu_closed_callback_; + DISALLOW_COPY_AND_ASSIGN(MenuModelAdapter); }; diff --git a/chromium/ui/views/controls/menu/menu_runner.h b/chromium/ui/views/controls/menu/menu_runner.h index 4011ece96da..ea2438ee980 100644 --- a/chromium/ui/views/controls/menu/menu_runner.h +++ b/chromium/ui/views/controls/menu/menu_runner.h @@ -120,7 +120,7 @@ class VIEWS_EXPORT MenuRunner { MenuButton* button, const gfx::Rect& bounds, MenuAnchorPosition anchor, - ui::MenuSourceType source_type) WARN_UNUSED_RESULT; + ui::MenuSourceType source_type); // Returns true if we're in a nested message loop running the menu. bool IsRunning() const; diff --git a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm index d25a6628b47..73e02eca385 100644 --- a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm +++ b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm @@ -23,6 +23,10 @@ class TestModel : public ui::SimpleMenuModel { void set_checked_command(int command) { checked_command_ = command; } + void set_menu_open_callback(const base::Closure& callback) { + menu_open_callback_ = callback; + } + private: class Delegate : public ui::SimpleMenuModel::Delegate { public: @@ -37,6 +41,10 @@ class TestModel : public ui::SimpleMenuModel { } void ExecuteCommand(int command_id, int event_flags) override {} + void MenuWillShow(SimpleMenuModel* source) override { + model_->menu_open_callback_.Run(); + } + private: TestModel* model_; @@ -46,6 +54,7 @@ class TestModel : public ui::SimpleMenuModel { private: int checked_command_ = -1; Delegate delegate_; + base::Closure menu_open_callback_; DISALLOW_COPY_AND_ASSIGN(TestModel); }; @@ -89,14 +98,13 @@ class MenuRunnerCocoaTest : public ViewsTestBase { ViewsTestBase::TearDown(); } - // Runs the menu after scheduling |block| on the run loop. - MenuRunner::RunResult RunMenu(dispatch_block_t block) { - CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopCommonModes, ^{ - EXPECT_TRUE(runner_->IsRunning()); - block(); - }); - return runner_->RunMenuAt(parent_, NULL, gfx::Rect(), MENU_ANCHOR_TOPLEFT, - MenuRunner::CONTEXT_MENU); + // Runs the menu after registering |callback| as the menu open callback. + MenuRunner::RunResult RunMenu(const base::Closure& callback) { + menu_->set_menu_open_callback( + base::Bind(&MenuRunnerCocoaTest::RunMenuWrapperCallback, + base::Unretained(this), callback)); + return runner_->RunMenuAt(parent_, nullptr, gfx::Rect(), + MENU_ANCHOR_TOPLEFT, MenuRunner::CONTEXT_MENU); } // Runs then cancels a combobox menu and captures the frame of the anchoring @@ -107,12 +115,10 @@ class MenuRunnerCocoaTest : public ViewsTestBase { // Should be one child (the compositor layer) before showing, and it should // go up by one (the anchor view) while the menu is shown. EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); - CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopCommonModes, ^{ - NSArray* subviews = [parent_->GetNativeView() subviews]; - EXPECT_EQ(2u, [subviews count]); - last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; - runner_->Cancel(); - }); + + menu_->set_menu_open_callback(base::Bind( + &MenuRunnerCocoaTest::RunMenuAtCallback, base::Unretained(this))); + MenuRunner::RunResult result = runner_->RunMenuAt( parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, MenuRunner::COMBOBOX); @@ -121,6 +127,16 @@ class MenuRunnerCocoaTest : public ViewsTestBase { return result; } + void MenuCancelCallback() { + runner_->Cancel(); + EXPECT_FALSE(runner_->IsRunning()); + } + + void MenuDeleteCallback() { + runner_->Release(); + runner_ = nullptr; + } + protected: scoped_ptr<TestModel> menu_; internal::MenuRunnerImplCocoa* runner_ = nullptr; @@ -128,16 +144,26 @@ class MenuRunnerCocoaTest : public ViewsTestBase { NSRect last_anchor_frame_ = NSZeroRect; private: + void RunMenuWrapperCallback(const base::Closure& callback) { + EXPECT_TRUE(runner_->IsRunning()); + callback.Run(); + } + + void RunMenuAtCallback() { + NSArray* subviews = [parent_->GetNativeView() subviews]; + EXPECT_EQ(2u, [subviews count]); + last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; + runner_->Cancel(); + } + DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); }; TEST_F(MenuRunnerCocoaTest, RunMenuAndCancel) { base::TimeDelta min_time = ui::EventTimeForNow(); - MenuRunner::RunResult result = RunMenu(^{ - runner_->Cancel(); - EXPECT_FALSE(runner_->IsRunning()); - }); + MenuRunner::RunResult result = RunMenu(base::Bind( + &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); EXPECT_FALSE(runner_->IsRunning()); @@ -151,19 +177,15 @@ TEST_F(MenuRunnerCocoaTest, RunMenuAndCancel) { } TEST_F(MenuRunnerCocoaTest, RunMenuAndDelete) { - MenuRunner::RunResult result = RunMenu(^{ - runner_->Release(); - runner_ = NULL; - }); - + MenuRunner::RunResult result = RunMenu(base::Bind( + &MenuRunnerCocoaTest::MenuDeleteCallback, base::Unretained(this))); EXPECT_EQ(MenuRunner::MENU_DELETED, result); } TEST_F(MenuRunnerCocoaTest, RunMenuTwice) { for (int i = 0; i < 2; ++i) { - MenuRunner::RunResult result = RunMenu(^{ - runner_->Cancel(); - }); + MenuRunner::RunResult result = RunMenu(base::Bind( + &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); EXPECT_FALSE(runner_->IsRunning()); } diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.cc b/chromium/ui/views/controls/menu/menu_runner_impl.cc index d02f7de1eff..b48edcdd129 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl.cc +++ b/chromium/ui/views/controls/menu/menu_runner_impl.cc @@ -88,8 +88,12 @@ MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent, if (!controller->IsBlockingRun()) { controller->CancelAll(); controller = NULL; + } else { + // Only nest the delegate when not cancelling drag-and-drop. When + // cancelling this will become the root delegate of the new + // MenuController + controller->AddNestedDelegate(this); } - controller->AddNestedDelegate(this); } else { // There's some other menu open and we're not nested. Cancel the menu. controller->CancelAll(); diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm index 47547814cf8..454c951817b 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm +++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm @@ -4,6 +4,7 @@ #import "ui/views/controls/menu/menu_runner_impl_cocoa.h" +#include "base/mac/sdk_forward_declarations.h" #import "ui/base/cocoa/menu_controller.h" #include "ui/base/models/menu_model.h" #include "ui/events/event_utils.h" @@ -41,7 +42,7 @@ base::scoped_nsobject<NSView> CreateMenuAnchorView( const gfx::Rect& screen_bounds, NSMenuItem* checked_item) { NSRect rect = gfx::ScreenRectToNSRect(screen_bounds); - rect.origin = [window convertScreenToBase:rect.origin]; + rect = [window convertRectFromScreen:rect]; rect = [[window contentView] convertRect:rect fromView:nil]; // If there's no checked item (e.g. Combobox::STYLE_ACTION), NSMenu will diff --git a/chromium/ui/views/controls/menu/menu_runner_unittest.cc b/chromium/ui/views/controls/menu/menu_runner_unittest.cc index b8e1528931e..49ba8832a1e 100644 --- a/chromium/ui/views/controls/menu/menu_runner_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_runner_unittest.cc @@ -8,10 +8,13 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" #include "ui/base/ui_base_types.h" +#include "ui/events/test/event_generator.h" #include "ui/views/controls/menu/menu_delegate.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_types.h" +#include "ui/views/controls/menu/submenu_view.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" @@ -19,22 +22,28 @@ namespace views { namespace test { // Implementation of MenuDelegate that only reports the values of calls to -// OnMenuClosed. +// OnMenuClosed and ExecuteCommand. class TestMenuDelegate : public MenuDelegate { public: TestMenuDelegate(); ~TestMenuDelegate() override; - int on_menu_closed_called() { return on_menu_closed_called_; } - MenuItemView* on_menu_closed_menu() { return on_menu_closed_menu_; } - MenuRunner::RunResult on_menu_closed_run_result() { + int execute_command_id() const { return execute_command_id_; } + + int on_menu_closed_called() const { return on_menu_closed_called_; } + MenuItemView* on_menu_closed_menu() const { return on_menu_closed_menu_; } + MenuRunner::RunResult on_menu_closed_run_result() const { return on_menu_closed_run_result_; } // MenuDelegate: + void ExecuteCommand(int id) override; void OnMenuClosed(MenuItemView* menu, MenuRunner::RunResult result) override; private: + // ID of last executed command. + int execute_command_id_; + // The number of times OnMenuClosed was called. int on_menu_closed_called_; @@ -46,12 +55,17 @@ class TestMenuDelegate : public MenuDelegate { }; TestMenuDelegate::TestMenuDelegate() - : on_menu_closed_called_(0), + : execute_command_id_(0), + on_menu_closed_called_(0), on_menu_closed_menu_(nullptr), on_menu_closed_run_result_(MenuRunner::MENU_DELETED) {} TestMenuDelegate::~TestMenuDelegate() {} +void TestMenuDelegate::ExecuteCommand(int id) { + execute_command_id_ = id; +} + void TestMenuDelegate::OnMenuClosed(MenuItemView* menu, MenuRunner::RunResult result) { on_menu_closed_called_++; @@ -100,6 +114,9 @@ void MenuRunnerTest::SetUp() { ViewsTestBase::SetUp(); menu_delegate_.reset(new TestMenuDelegate); menu_item_view_ = new MenuItemView(menu_delegate_.get()); + menu_item_view_->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); + menu_item_view_->AppendMenuItemWithLabel(2, + base::WideToUTF16(L"\x062f\x0648")); owner_.reset(new Widget); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); @@ -132,5 +149,91 @@ TEST_F(MenuRunnerTest, AsynchronousRun) { EXPECT_EQ(MenuRunner::NORMAL_EXIT, delegate->on_menu_closed_run_result()); } +// Tests that when a menu is run asynchronously, key events are handled properly +// by testing that Escape key closes the menu. +TEST_F(MenuRunnerTest, AsynchronousKeyEventHandling) { + InitMenuRunner(MenuRunner::ASYNC); + MenuRunner* runner = menu_runner(); + MenuRunner::RunResult result = runner->RunMenuAt( + owner(), nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_NONE); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); + EXPECT_TRUE(runner->IsRunning()); + + ui::test::EventGenerator generator(GetContext(), owner()->GetNativeWindow()); + generator.PressKey(ui::VKEY_ESCAPE, 0); + EXPECT_FALSE(runner->IsRunning()); + TestMenuDelegate* delegate = menu_delegate(); + EXPECT_EQ(1, delegate->on_menu_closed_called()); + EXPECT_EQ(nullptr, delegate->on_menu_closed_menu()); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, delegate->on_menu_closed_run_result()); +} + +// Tests that a key press on a US keyboard layout activates the correct menu +// item. +TEST_F(MenuRunnerTest, LatinMnemonic) { + InitMenuRunner(MenuRunner::ASYNC); + MenuRunner* runner = menu_runner(); + MenuRunner::RunResult result = runner->RunMenuAt( + owner(), nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_NONE); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); + EXPECT_TRUE(runner->IsRunning()); + + ui::test::EventGenerator generator(GetContext(), owner()->GetNativeWindow()); + generator.PressKey(ui::VKEY_O, 0); + EXPECT_FALSE(runner->IsRunning()); + TestMenuDelegate* delegate = menu_delegate(); + EXPECT_EQ(1, delegate->execute_command_id()); + EXPECT_EQ(1, delegate->on_menu_closed_called()); + EXPECT_NE(nullptr, delegate->on_menu_closed_menu()); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, delegate->on_menu_closed_run_result()); +} + +// Tests that a key press on a non-US keyboard layout activates the correct menu +// item. +TEST_F(MenuRunnerTest, NonLatinMnemonic) { + InitMenuRunner(MenuRunner::ASYNC); + MenuRunner* runner = menu_runner(); + MenuRunner::RunResult result = runner->RunMenuAt( + owner(), nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_NONE); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); + EXPECT_TRUE(runner->IsRunning()); + + ui::test::EventGenerator generator(GetContext(), owner()->GetNativeWindow()); + ui::KeyEvent key_press(0x062f, ui::VKEY_N, 0); + generator.Dispatch(&key_press); + EXPECT_FALSE(runner->IsRunning()); + TestMenuDelegate* delegate = menu_delegate(); + EXPECT_EQ(2, delegate->execute_command_id()); + EXPECT_EQ(1, delegate->on_menu_closed_called()); + EXPECT_NE(nullptr, delegate->on_menu_closed_menu()); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, delegate->on_menu_closed_run_result()); +} + +// Tests that attempting to nest a menu within a drag-and-drop menu does not +// cause a crash. Instead the drag and drop action should be canceled, and the +// new menu should be openned. +TEST_F(MenuRunnerTest, NestingDuringDrag) { + InitMenuRunner(MenuRunner::FOR_DROP); + MenuRunner* runner = menu_runner(); + MenuRunner::RunResult result = runner->RunMenuAt( + owner(), nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_NONE); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); + EXPECT_TRUE(runner->IsRunning()); + + scoped_ptr<TestMenuDelegate> nested_delegate(new TestMenuDelegate); + MenuItemView* nested_menu = new MenuItemView(nested_delegate.get()); + scoped_ptr<MenuRunner> nested_runner( + new MenuRunner(nested_menu, MenuRunner::IS_NESTED | MenuRunner::ASYNC)); + result = nested_runner->RunMenuAt(owner(), nullptr, gfx::Rect(), + MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_NONE); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); + EXPECT_TRUE(nested_runner->IsRunning()); + EXPECT_FALSE(runner->IsRunning()); + TestMenuDelegate* delegate = menu_delegate(); + EXPECT_EQ(1, delegate->on_menu_closed_called()); + EXPECT_NE(nullptr, delegate->on_menu_closed_menu()); + EXPECT_EQ(MenuRunner::NORMAL_EXIT, delegate->on_menu_closed_run_result()); +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc index 05c8caedb55..381bef70f6f 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc @@ -76,7 +76,6 @@ class MenuScrollButton : public View { // The background. gfx::Rect item_bounds(0, 0, width(), height()); NativeTheme::ExtraParams extra; - extra.menu_item.is_selected = false; GetNativeTheme()->Paint(canvas->sk_canvas(), NativeTheme::kMenuItemBackground, NativeTheme::kNormal, item_bounds, extra); @@ -263,9 +262,6 @@ void MenuScrollViewContainer::GetAccessibleState( // Now change the role. state->role = ui::AX_ROLE_MENU_BAR; - // Some AT (like NVDA) will not process focus events on menu item children - // unless a parent claims to be focused. - state->AddStateFlag(ui::AX_STATE_FOCUSED); } void MenuScrollViewContainer::OnBoundsChanged( diff --git a/chromium/ui/views/controls/menu/menu_separator_views.cc b/chromium/ui/views/controls/menu/menu_separator_views.cc index 357bad8b646..6baae9280cc 100644 --- a/chromium/ui/views/controls/menu/menu_separator_views.cc +++ b/chromium/ui/views/controls/menu/menu_separator_views.cc @@ -10,12 +10,6 @@ #include "ui/native_theme/native_theme.h" #include "ui/views/controls/menu/menu_config.h" -namespace { - -const int kSeparatorHeight = 1; - -} // namespace - namespace views { #if !defined(OS_WIN) @@ -47,9 +41,11 @@ gfx::Size MenuSeparator::GetPreferredSize() const { gfx::Rect MenuSeparator::GetPaintBounds() { int pos = 0; + const MenuConfig& menu_config = MenuConfig::instance(); + int separator_thickness = menu_config.separator_thickness; switch (type_) { case ui::LOWER_SEPARATOR: - pos = height() - kSeparatorHeight; + pos = height() - separator_thickness; break; case ui::SPACING_SEPARATOR: return gfx::Rect(); @@ -60,8 +56,8 @@ gfx::Rect MenuSeparator::GetPaintBounds() { break; } - gfx::Rect paint_rect(0, pos, width(), kSeparatorHeight); - if (MenuConfig::instance().use_outer_border) + gfx::Rect paint_rect(0, pos, width(), separator_thickness); + if (menu_config.use_outer_border) paint_rect.Inset(1, 0); return paint_rect; } 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 2a7d914d684..6bf4ce8a3db 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura.cc @@ -138,7 +138,7 @@ void NativeViewHostAura::InstallClip(int x, int y, int w, int h) { } bool NativeViewHostAura::HasInstalledClip() { - return clip_rect_; + return !!clip_rect_; } void NativeViewHostAura::UninstallClip() { 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 da057fa743a..b3e0cfcd4b6 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac.mm +++ b/chromium/ui/views/controls/native/native_view_host_mac.mm @@ -6,18 +6,16 @@ #import <Cocoa/Cocoa.h> -#include "base/logging.h" #include "base/mac/foundation_util.h" -#import "ui/views/cocoa/bridged_content_view.h" +#import "ui/views/cocoa/bridged_native_widget.h" #include "ui/views/controls/native/native_view_host.h" +#include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/widget.h" namespace views { namespace { -// Reparents |native_view| to be a child of the native content view of -// |new_parent|. -void ReparentNSView(NSView* native_view, Widget* new_parent) { +void EnsureNativeViewHasNoChildWidgets(NSView* native_view) { DCHECK(native_view); // Mac's NativeViewHost has no support for hosting its own child widgets. // This check is probably overly restrictive, since the Widget containing the @@ -29,17 +27,6 @@ void ReparentNSView(NSView* native_view, Widget* new_parent) { Widget::GetAllChildWidgets(native_view, &child_widgets); CHECK_GE(1u, child_widgets.size()); // 1 (itself) or 0 if detached. } - - if (!new_parent) { - [native_view removeFromSuperview]; - return; - } - - BridgedContentView* new_superview = - base::mac::ObjCCastStrict<BridgedContentView>( - new_parent->GetNativeView()); - DCHECK(new_superview); - [new_superview addSubview:native_view]; } } // namespace @@ -57,7 +44,13 @@ void NativeViewHostMac::AttachNativeView() { DCHECK(host_->native_view()); DCHECK(!native_view_); native_view_.reset([host_->native_view() retain]); - ReparentNSView(host_->native_view(), host_->GetWidget()); + + EnsureNativeViewHasNoChildWidgets(native_view_); + BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow( + host_->GetWidget()->GetNativeWindow()); + DCHECK(bridge); + [bridge->ns_view() addSubview:native_view_]; + bridge->SetAssociationForView(host_, native_view_); } void NativeViewHostMac::NativeViewDetaching(bool destroyed) { @@ -70,7 +63,15 @@ void NativeViewHostMac::NativeViewDetaching(bool destroyed) { // NativeViewHost::Detach(). DCHECK(!native_view_ || native_view_ == host_->native_view()); [host_->native_view() setHidden:YES]; - ReparentNSView(host_->native_view(), NULL); + [host_->native_view() removeFromSuperview]; + + EnsureNativeViewHasNoChildWidgets(host_->native_view()); + BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow( + host_->GetWidget()->GetNativeWindow()); + // BridgedNativeWidget can be null when Widget is closing. + if (bridge) + bridge->ClearAssociationForView(host_); + native_view_.reset(); } diff --git a/chromium/ui/views/controls/progress_bar.cc b/chromium/ui/views/controls/progress_bar.cc index 0a9fd12a4a3..0278e5b51b0 100644 --- a/chromium/ui/views/controls/progress_bar.cc +++ b/chromium/ui/views/controls/progress_bar.cc @@ -82,9 +82,8 @@ void FillRoundRect(gfx::Canvas* canvas, } else { p[1].iset(x, y + h); } - skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear( - p, colors, points, count, SkShader::kClamp_TileMode)); - paint.setShader(s.get()); + paint.setShader(SkGradientShader::MakeLinear(p, colors, points, count, + SkShader::kClamp_TileMode)); canvas->DrawPath(path, paint); } @@ -296,11 +295,9 @@ void ProgressBar::OnPaint(gfx::Canvas* canvas) { std::max(0, progress_width - kHighlightWidth - kBorderWidth); p[0].iset(highlight_left, 0); p[1].iset(progress_width, 0); - skia::RefPtr<SkShader> s = - skia::AdoptRef(SkGradientShader::CreateLinear( - p, highlight_colors, highlight_points, - arraysize(highlight_colors), SkShader::kClamp_TileMode)); - paint.setShader(s.get()); + paint.setShader(SkGradientShader::MakeLinear( + p, highlight_colors, highlight_points, arraysize(highlight_colors), + SkShader::kClamp_TileMode)); paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); canvas->DrawRect(gfx::Rect(highlight_left, 0, kHighlightWidth + kBorderWidth, bar_height), diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index 3dae5a3897d..a3e1e9787a2 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -10,7 +10,7 @@ #include "ui/gfx/canvas.h" #include "ui/native_theme/native_theme.h" #include "ui/views/border.h" -#include "ui/views/controls/scrollbar/native_scroll_bar.h" +#include "ui/views/style/platform_style.h" #include "ui/views/widget/root_view.h" namespace views { @@ -124,8 +124,8 @@ ScrollView::ScrollView() contents_viewport_(new Viewport()), header_(NULL), header_viewport_(new Viewport()), - horiz_sb_(new NativeScrollBar(true)), - vert_sb_(new NativeScrollBar(false)), + horiz_sb_(PlatformStyle::CreateScrollBar(true).release()), + vert_sb_(PlatformStyle::CreateScrollBar(false).release()), corner_view_(new ScrollCornerView()), min_height_(-1), max_height_(-1), @@ -300,26 +300,28 @@ void ScrollView::Layout() { should_layout_contents = true; } + int height_offset = horiz_sb_required ? + horiz_sb_->GetContentOverlapSize() : 0; + int width_offset = vert_sb_required ? + vert_sb_->GetContentOverlapSize() : 0; + if (horiz_sb_required) { - int height_offset = horiz_sb_->GetContentOverlapSize(); - horiz_sb_->SetBounds(0, + horiz_sb_->SetBounds(contents_x, viewport_bounds.bottom() - height_offset, - viewport_bounds.right(), + viewport_bounds.right() - contents_x - width_offset, horiz_sb_height + height_offset); } if (vert_sb_required) { int width_offset = vert_sb_->GetContentOverlapSize(); vert_sb_->SetBounds(viewport_bounds.right() - width_offset, - 0, + contents_y, vert_sb_width + width_offset, - viewport_bounds.bottom()); + viewport_bounds.bottom() - contents_y - height_offset); } if (corner_view_required) { // Show the resize corner. - corner_view_->SetBounds(viewport_bounds.right(), - viewport_bounds.bottom(), - vert_sb_width, - horiz_sb_height); + corner_view_->SetBounds(vert_sb_->bounds().x(), horiz_sb_->bounds().y(), + vert_sb_width, horiz_sb_height); } // Update to the real client size with the visible scrollbars. diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc index c8137095a94..011f65d38a1 100644 --- a/chromium/ui/views/controls/scroll_view_unittest.cc +++ b/chromium/ui/views/controls/scroll_view_unittest.cc @@ -6,9 +6,14 @@ #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/border.h" #include "ui/views/controls/scrollbar/overlay_scroll_bar.h" #include "ui/views/test/test_views.h" +#if defined(OS_MACOSX) +#include "ui/base/test/scoped_preferred_scroller_style_mac.h" +#endif + namespace views { namespace { @@ -17,6 +22,8 @@ const int kWidth = 100; const int kMinHeight = 50; const int kMaxHeight = 100; +enum ScrollBarOrientation { HORIZONTAL, VERTICAL }; + // View implementation that allows setting the preferred size. class CustomView : public View { public: @@ -46,6 +53,20 @@ class CustomView : public View { DISALLOW_COPY_AND_ASSIGN(CustomView); }; +void CheckScrollbarVisibility(const ScrollView& scroll_view, + ScrollBarOrientation orientation, + bool should_be_visible) { + const ScrollBar* scrollbar = orientation == HORIZONTAL + ? scroll_view.horizontal_scroll_bar() + : scroll_view.vertical_scroll_bar(); + if (should_be_visible) { + ASSERT_TRUE(scrollbar); + EXPECT_TRUE(scrollbar->visible()); + } else { + EXPECT_TRUE(!scrollbar || !scrollbar->visible()); + } +} + } // namespace // Verifies the viewport is sized to fit the available space. @@ -59,7 +80,12 @@ TEST(ScrollViewTest, ViewportSizedToFit) { } // Verifies the scrollbars are added as necessary. +// If on Mac, test the non-overlay scrollbars. TEST(ScrollViewTest, ScrollBars) { +#if defined(OS_MACOSX) + ui::test::ScopedPreferredScrollerStyle scroller_style_override(false); +#endif + ScrollView scroll_view; View* contents = new View; scroll_view.SetContents(contents); @@ -70,6 +96,8 @@ TEST(ScrollViewTest, ScrollBars) { scroll_view.Layout(); EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), contents->parent()->width()); EXPECT_EQ(100, contents->parent()->height()); + CheckScrollbarVisibility(scroll_view, VERTICAL, true); + CheckScrollbarVisibility(scroll_view, HORIZONTAL, false); EXPECT_TRUE(!scroll_view.horizontal_scroll_bar() || !scroll_view.horizontal_scroll_bar()->visible()); ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL); @@ -81,10 +109,8 @@ TEST(ScrollViewTest, ScrollBars) { EXPECT_EQ(100, contents->parent()->width()); EXPECT_EQ(100 - scroll_view.GetScrollBarHeight(), contents->parent()->height()); - ASSERT_TRUE(scroll_view.horizontal_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view.horizontal_scroll_bar()->visible()); - EXPECT_TRUE(!scroll_view.vertical_scroll_bar() || - !scroll_view.vertical_scroll_bar()->visible()); + CheckScrollbarVisibility(scroll_view, VERTICAL, false); + CheckScrollbarVisibility(scroll_view, HORIZONTAL, true); // Both horizontal and vertical. contents->SetBounds(0, 0, 300, 400); @@ -92,10 +118,79 @@ TEST(ScrollViewTest, ScrollBars) { EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), contents->parent()->width()); EXPECT_EQ(100 - scroll_view.GetScrollBarHeight(), contents->parent()->height()); + CheckScrollbarVisibility(scroll_view, VERTICAL, true); + CheckScrollbarVisibility(scroll_view, HORIZONTAL, true); + + // Add a border, test vertical scrollbar. + const int kTopPadding = 1; + const int kLeftPadding = 2; + const int kBottomPadding = 3; + const int kRightPadding = 4; + scroll_view.SetBorder(Border::CreateEmptyBorder( + kTopPadding, kLeftPadding, kBottomPadding, kRightPadding)); + contents->SetBounds(0, 0, 50, 400); + scroll_view.Layout(); + EXPECT_EQ( + 100 - scroll_view.GetScrollBarWidth() - kLeftPadding - kRightPadding, + contents->parent()->width()); + EXPECT_EQ(100 - kTopPadding - kBottomPadding, contents->parent()->height()); + EXPECT_TRUE(!scroll_view.horizontal_scroll_bar() || + !scroll_view.horizontal_scroll_bar()->visible()); + ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible()); + gfx::Rect bounds = scroll_view.vertical_scroll_bar()->bounds(); + EXPECT_EQ(100 - scroll_view.GetScrollBarWidth() - kRightPadding, bounds.x()); + EXPECT_EQ(100 - kRightPadding, bounds.right()); + EXPECT_EQ(kTopPadding, bounds.y()); + EXPECT_EQ(100 - kBottomPadding, bounds.bottom()); + + // Horizontal with border. + contents->SetBounds(0, 0, 400, 50); + scroll_view.Layout(); + EXPECT_EQ(100 - kLeftPadding - kRightPadding, contents->parent()->width()); + EXPECT_EQ( + 100 - scroll_view.GetScrollBarHeight() - kTopPadding - kBottomPadding, + contents->parent()->height()); ASSERT_TRUE(scroll_view.horizontal_scroll_bar() != NULL); EXPECT_TRUE(scroll_view.horizontal_scroll_bar()->visible()); + EXPECT_TRUE(!scroll_view.vertical_scroll_bar() || + !scroll_view.vertical_scroll_bar()->visible()); + bounds = scroll_view.horizontal_scroll_bar()->bounds(); + EXPECT_EQ(kLeftPadding, bounds.x()); + EXPECT_EQ(100 - kRightPadding, bounds.right()); + EXPECT_EQ(100 - kBottomPadding - scroll_view.GetScrollBarHeight(), + bounds.y()); + EXPECT_EQ(100 - kBottomPadding, bounds.bottom()); + + // Both horizontal and vertical with border. + contents->SetBounds(0, 0, 300, 400); + scroll_view.Layout(); + EXPECT_EQ( + 100 - scroll_view.GetScrollBarWidth() - kLeftPadding - kRightPadding, + contents->parent()->width()); + EXPECT_EQ( + 100 - scroll_view.GetScrollBarHeight() - kTopPadding - kBottomPadding, + contents->parent()->height()); + bounds = scroll_view.horizontal_scroll_bar()->bounds(); + // Check horiz. + ASSERT_TRUE(scroll_view.horizontal_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view.horizontal_scroll_bar()->visible()); + bounds = scroll_view.horizontal_scroll_bar()->bounds(); + EXPECT_EQ(kLeftPadding, bounds.x()); + EXPECT_EQ(100 - kRightPadding - scroll_view.GetScrollBarWidth(), + bounds.right()); + EXPECT_EQ(100 - kBottomPadding - scroll_view.GetScrollBarHeight(), + bounds.y()); + EXPECT_EQ(100 - kBottomPadding, bounds.bottom()); + // Check vert. ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL); EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible()); + bounds = scroll_view.vertical_scroll_bar()->bounds(); + EXPECT_EQ(100 - scroll_view.GetScrollBarWidth() - kRightPadding, bounds.x()); + EXPECT_EQ(100 - kRightPadding, bounds.right()); + EXPECT_EQ(kTopPadding, bounds.y()); + EXPECT_EQ(100 - kBottomPadding - scroll_view.GetScrollBarHeight(), + bounds.bottom()); } // Assertions around adding a header. @@ -152,6 +247,9 @@ TEST(ScrollViewTest, ScrollBarsWithHeader) { !scroll_view.horizontal_scroll_bar()->visible()); ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL); EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible()); + // Make sure the vertical scrollbar overlaps the header. + EXPECT_EQ(header->y(), scroll_view.vertical_scroll_bar()->y()); + EXPECT_EQ(header->y(), contents->y()); // Size the contents such that horizontal scrollbar is needed. contents->SetBounds(0, 0, 400, 50); @@ -374,4 +472,63 @@ TEST(ScrollViewTest, CornerViewVisibility) { EXPECT_TRUE(corner_view->visible()); } +#if defined(OS_MACOSX) +// Tests the overlay scrollbars on Mac. Ensure that they show up properly and +// do not overlap each other. +TEST(ScrollViewTest, CocoaOverlayScrollBars) { + scoped_ptr<ui::test::ScopedPreferredScrollerStyle> scroller_style_override; + scroller_style_override.reset( + new ui::test::ScopedPreferredScrollerStyle(true)); + ScrollView scroll_view; + View* contents = new View; + scroll_view.SetContents(contents); + scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); + + // Size the contents such that vertical scrollbar is needed. + // Since it is overlaid, the ViewPort size should match the ScrollView. + contents->SetBounds(0, 0, 50, 400); + scroll_view.Layout(); + EXPECT_EQ(100, contents->parent()->width()); + EXPECT_EQ(100, contents->parent()->height()); + EXPECT_EQ(0, scroll_view.GetScrollBarWidth()); + CheckScrollbarVisibility(scroll_view, VERTICAL, true); + CheckScrollbarVisibility(scroll_view, HORIZONTAL, false); + + // Size the contents such that horizontal scrollbar is needed. + contents->SetBounds(0, 0, 400, 50); + scroll_view.Layout(); + EXPECT_EQ(100, contents->parent()->width()); + EXPECT_EQ(100, contents->parent()->height()); + EXPECT_EQ(0, scroll_view.GetScrollBarHeight()); + CheckScrollbarVisibility(scroll_view, VERTICAL, false); + CheckScrollbarVisibility(scroll_view, HORIZONTAL, true); + + // Both horizontal and vertical scrollbars. + contents->SetBounds(0, 0, 300, 400); + scroll_view.Layout(); + EXPECT_EQ(100, contents->parent()->width()); + EXPECT_EQ(100, contents->parent()->height()); + EXPECT_EQ(0, scroll_view.GetScrollBarWidth()); + EXPECT_EQ(0, scroll_view.GetScrollBarHeight()); + CheckScrollbarVisibility(scroll_view, VERTICAL, true); + CheckScrollbarVisibility(scroll_view, HORIZONTAL, true); + + // Make sure the horizontal and vertical scrollbars don't overlap each other. + gfx::Rect vert_bounds = scroll_view.vertical_scroll_bar()->bounds(); + gfx::Rect horiz_bounds = scroll_view.horizontal_scroll_bar()->bounds(); + EXPECT_EQ(vert_bounds.x(), horiz_bounds.right()); + EXPECT_EQ(horiz_bounds.y(), vert_bounds.bottom()); + + // Switch to the non-overlay style and check that the ViewPort is now sized + // to be smaller, and ScrollbarWidth and ScrollbarHeight are non-zero. + scroller_style_override.reset( + new ui::test::ScopedPreferredScrollerStyle(false)); + EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), contents->parent()->width()); + EXPECT_EQ(100 - scroll_view.GetScrollBarHeight(), + contents->parent()->height()); + EXPECT_NE(0, scroll_view.GetScrollBarWidth()); + EXPECT_NE(0, scroll_view.GetScrollBarHeight()); +} +#endif + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc index 117fe345dff..e9333e1458c 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc @@ -413,7 +413,8 @@ void BaseScrollBar::Update(int viewport_size, // Thumb Height and Thumb Pos. // The height of the thumb is the ratio of the Viewport height to the // content size multiplied by the height of the thumb track. - double ratio = static_cast<double>(viewport_size) / contents_size_; + double ratio = + std::min(1.0, static_cast<double>(viewport_size) / contents_size_); int thumb_size = static_cast<int>(ratio * GetTrackSize()); thumb_->SetSize(thumb_size); @@ -495,18 +496,23 @@ int BaseScrollBar::CalculateThumbPosition(int contents_scroll_offset) const { // In some combination of viewport_size and contents_size_, the result of // simple division can be rounded and there could be 1 pixel gap even when the // contents scroll down to the bottom. See crbug.com/244671 - if (contents_scroll_offset + viewport_size_ == contents_size_) { - int track_size = GetTrackSize(); - return track_size - (viewport_size_ * GetTrackSize() / contents_size_); - } - return (contents_scroll_offset * GetTrackSize()) / contents_size_; + int thumb_max = GetTrackSize() - thumb_->GetSize(); + if (contents_scroll_offset + viewport_size_ == contents_size_) + return thumb_max; + return (contents_scroll_offset * thumb_max) / + (contents_size_ - viewport_size_); } int BaseScrollBar::CalculateContentsOffset(int thumb_position, bool scroll_to_middle) const { + int thumb_size = thumb_->GetSize(); + int track_size = GetTrackSize(); + if (track_size == thumb_size) + return 0; if (scroll_to_middle) - thumb_position = thumb_position - (thumb_->GetSize() / 2); - return (thumb_position * contents_size_) / GetTrackSize(); + thumb_position = thumb_position - (thumb_size / 2); + return (thumb_position * (contents_size_ - viewport_size_)) / + (track_size - thumb_size); } void BaseScrollBar::SetThumbTrackState(CustomButton::ButtonState state) { diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h index f54a8808178..178fdc1e908 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h @@ -115,6 +115,7 @@ class VIEWS_EXPORT BaseScrollBar : public ScrollBar, private: FRIEND_TEST_ALL_PREFIXES(NativeScrollBarTest, ScrollBarFitsToBottom); + FRIEND_TEST_ALL_PREFIXES(NativeScrollBarTest, ThumbFullLengthOfTrack); int GetThumbSizeForTest(); // Changes to 'pushed' state and starts a timer to scroll repeatedly. 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 54dbe7501ee..d3fb7071ed8 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc @@ -36,9 +36,7 @@ void BaseScrollBarButton::OnMouseCaptureLost() { void BaseScrollBarButton::RepeaterNotifyClick() { // TODO(sky): See if we can convert to using |Screen| everywhere. - // TODO(scottmg): Native is wrong: http://crbug.com/133312 - gfx::Point cursor_point = - gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(); + gfx::Point cursor_point = gfx::Screen::GetScreen()->GetCursorScreenPoint(); ui::MouseEvent event(ui::ET_MOUSE_RELEASED, cursor_point, cursor_point, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h new file mode 100644 index 00000000000..2cca52a6e38 --- /dev/null +++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h @@ -0,0 +1,121 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_CONTROLS_SCROLLBAR_COCOA_SCROLL_BAR_H_ +#define UI_VIEWS_CONTROLS_SCROLLBAR_COCOA_SCROLL_BAR_H_ + +#include "base/macros.h" +#import "base/mac/scoped_nsobject.h" +#include "ui/compositor/layer_animation_observer.h" +#include "ui/gfx/animation/slide_animation.h" +#import "ui/views/cocoa/views_scrollbar_bridge.h" +#include "ui/views/controls/scrollbar/base_scroll_bar.h" +#include "ui/views/views_export.h" + +namespace views { + +class CocoaScrollBarThumb; + +// The transparent scrollbar for Mac which overlays its contents. +class VIEWS_EXPORT CocoaScrollBar : public BaseScrollBar, + public ViewsScrollbarBridgeDelegate, + public ui::ImplicitAnimationObserver, + public gfx::AnimationDelegate { + public: + explicit CocoaScrollBar(bool horizontal); + ~CocoaScrollBar() override; + + // BaseScrollBar: + void ScrollToPosition(int position) override; + + // ViewsScrollbarBridgeDelegate: + void OnScrollerStyleChanged() override; + + // View: + bool OnMousePressed(const ui::MouseEvent& event) override; + void OnMouseReleased(const ui::MouseEvent& event) override; + void OnMouseEntered(const ui::MouseEvent& event) override; + void OnMouseExited(const ui::MouseEvent& event) override; + + // ui::ImplicitAnimationObserver: + void OnImplicitAnimationsCompleted() override; + + // gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; + + // Returns the scroller style. + NSScrollerStyle GetScrollerStyle() const { return scroller_style_; } + + // Returns true if the opacity is 0.0. + bool IsScrollbarFullyHidden() const; + + protected: + // BaseScrollBar: + gfx::Rect GetTrackBounds() const override; + + // ScrollBar: + int GetLayoutSize() const override; + int GetContentOverlapSize() const override; + + // View: + void Layout() override; + gfx::Size GetPreferredSize() const override; + void OnPaint(gfx::Canvas* canvas) override; + + private: + // Methods to change the visibility of the scrollbar. + void ShowScrollbar(); + void HideScrollbar(); + + // Returns true if the scrollbar is in a hover or pressed state. + bool IsHoverOrPressedState() const; + + // Updates the thickness of the scrollbar according to the current state of + // the expand animation. + void UpdateScrollbarThickness(); + + // Resets the scrolltrack and the thickness if the scrollbar is not hidden + // and is not in a hover/pressed state. + void ResetOverlayScrollbar(); + + // Returns the thickness of the scrollbar. + int ScrollbarThickness() const; + + // Sets the scrolltrack's visibility and then repaints it. + void SetScrolltrackVisible(bool visible); + + // Converts GetThumb() into a CocoaScrollBarThumb object and returns it. + CocoaScrollBarThumb* GetCocoaScrollBarThumb() const; + + // Scroller style the scrollbar is using. + NSScrollerStyle scroller_style_; + + // Timer that will start the scrollbar's hiding animation when it reaches 0. + base::Timer hide_scrollbar_timer_; + + // Slide animation that animates the thickness of an overlay scrollbar. + // The animation expands the scrollbar as the showing animation and shrinks + // the scrollbar as the hiding animation. + gfx::SlideAnimation thickness_animation_; + + // True when the scrollbar is expanded. + bool is_expanded_; + + // True when the scrolltrack should be drawn. + bool has_scrolltrack_; + + // True when the scrollbar has started dragging since it was last shown. + // This is set to false when we begin to hide the scrollbar. + bool did_start_dragging_; + + // The bridge for NSScroller. + base::scoped_nsobject<ViewsScrollbarBridge> bridge_; + + DISALLOW_COPY_AND_ASSIGN(CocoaScrollBar); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_SCROLLBAR_COCOA_SCROLL_BAR_H_ diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm new file mode 100644 index 00000000000..f11ea1a237d --- /dev/null +++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm @@ -0,0 +1,487 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ui/views/controls/scrollbar/cocoa_scroll_bar.h" + +#import "base/mac/sdk_forward_declarations.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/effects/SkGradientShader.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animator.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/canvas.h" +#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" + +namespace views { + +namespace { + +// The length of the fade animation. +const int kFadeDurationMs = 240; + +// The length of the expand animation. +const int kExpandDurationMs = 240; + +// How long we should wait before hiding the scrollbar. +const int kScrollbarHideTimeoutMs = 500; + +// The thickness of the normal and expanded scrollbars. +const int kScrollbarThickness = 12; +const int kExpandedScrollbarThickness = 16; + +// The thickness of the scrollbar thumb. +const int kScrollbarThumbThickness = 8; + +// The width of the scroller track border. +const int kScrollerTrackBorderWidth = 1; + +// The amount the thumb is inset from both the ends and the sides of the normal +// and expanded tracks. +const int kScrollbarThumbInset = 2; +const int kExpandedScrollbarThumbInset = 3; + +// Scrollbar thumb colors. +const SkColor kScrollerDefaultThumbColor = SkColorSetARGB(0x38, 0, 0, 0); +const SkColor kScrollerHoverThumbColor = SkColorSetARGB(0x80, 0, 0, 0); + +// Opacity of the overlay scrollbar. +const float kOverlayOpacity = 0.8f; + +// Scroller track colors. +const SkColor kScrollerTrackGradientColors[] = { + SkColorSetRGB(0xEF, 0xEF, 0xEF), SkColorSetRGB(0xF9, 0xF9, 0xF9), + SkColorSetRGB(0xFD, 0xFD, 0xFD), SkColorSetRGB(0xF6, 0xF6, 0xF6)}; +const SkColor kScrollerTrackInnerBorderColor = SkColorSetRGB(0xE4, 0xE4, 0xE4); +const SkColor kScrollerTrackOuterBorderColor = SkColorSetRGB(0xEF, 0xEF, 0xEF); + +} // namespace + +////////////////////////////////////////////////////////////////// +// CocoaScrollBarThumb + +class CocoaScrollBarThumb : public BaseScrollBarThumb { + public: + explicit CocoaScrollBarThumb(CocoaScrollBar* scroll_bar); + ~CocoaScrollBarThumb() override; + + // Returns true if the thumb is in hovered state. + bool IsStateHovered() const; + + // Returns true if the thumb is in pressed state. + bool IsStatePressed() const; + + protected: + // View: + gfx::Size GetPreferredSize() const override; + void OnPaint(gfx::Canvas* canvas) override; + bool OnMousePressed(const ui::MouseEvent& event) override; + void OnMouseReleased(const ui::MouseEvent& event) override; + void OnMouseEntered(const ui::MouseEvent& event) override; + void OnMouseExited(const ui::MouseEvent& event) override; + + private: + // Converts scroll_bar() into a CocoaScrollBar object and returns it. + CocoaScrollBar* cocoa_scroll_bar() { + return static_cast<CocoaScrollBar*>(scroll_bar()); + } + + DISALLOW_COPY_AND_ASSIGN(CocoaScrollBarThumb); +}; + +CocoaScrollBarThumb::CocoaScrollBarThumb(CocoaScrollBar* scroll_bar) + : BaseScrollBarThumb(scroll_bar) { + DCHECK(scroll_bar); + + // This is necessary, otherwise the thumb will be rendered below the views if + // those views paint to their own layers. + SetPaintToLayer(true); + layer()->SetFillsBoundsOpaquely(false); +} + +CocoaScrollBarThumb::~CocoaScrollBarThumb() {} + +bool CocoaScrollBarThumb::IsStateHovered() const { + return GetState() == CustomButton::STATE_HOVERED; +} + +bool CocoaScrollBarThumb::IsStatePressed() const { + return GetState() == CustomButton::STATE_PRESSED; +} + +gfx::Size CocoaScrollBarThumb::GetPreferredSize() const { + return gfx::Size(kScrollbarThumbThickness, kScrollbarThumbThickness); +} + +void CocoaScrollBarThumb::OnPaint(gfx::Canvas* canvas) { + SkColor thumb_color = kScrollerDefaultThumbColor; + if (cocoa_scroll_bar()->GetScrollerStyle() == NSScrollerStyleOverlay || + IsStateHovered() || + IsStatePressed()) { + thumb_color = kScrollerHoverThumbColor; + } + + gfx::Rect local_bounds(GetLocalBounds()); + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(thumb_color); + const SkScalar radius = + std::min(local_bounds.width(), local_bounds.height()); + canvas->DrawRoundRect(local_bounds, radius, paint); +} + +bool CocoaScrollBarThumb::OnMousePressed(const ui::MouseEvent& event) { + // Ignore the mouse press if the scrollbar is hidden. + if (cocoa_scroll_bar()->IsScrollbarFullyHidden()) + return false; + + return BaseScrollBarThumb::OnMousePressed(event); +} + +void CocoaScrollBarThumb::OnMouseReleased(const ui::MouseEvent& event) { + BaseScrollBarThumb::OnMouseReleased(event); + scroll_bar()->OnMouseReleased(event); +} + +void CocoaScrollBarThumb::OnMouseEntered(const ui::MouseEvent& event) { + BaseScrollBarThumb::OnMouseEntered(event); + scroll_bar()->OnMouseEntered(event); +} + +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); +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar class + +CocoaScrollBar::CocoaScrollBar(bool horizontal) + : BaseScrollBar(horizontal, new CocoaScrollBarThumb(this)), + hide_scrollbar_timer_( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kScrollbarHideTimeoutMs), + base::Bind(&CocoaScrollBar::HideScrollbar, base::Unretained(this)), + false), + thickness_animation_(this), + is_expanded_(false), + did_start_dragging_(false) { + bridge_.reset([[ViewsScrollbarBridge alloc] initWithDelegate:this]); + scroller_style_ = [ViewsScrollbarBridge getPreferredScrollerStyle]; + + thickness_animation_.SetSlideDuration(kExpandDurationMs); + + SetPaintToLayer(true); + has_scrolltrack_ = scroller_style_ == NSScrollerStyleLegacy; + layer()->SetOpacity(scroller_style_ == NSScrollerStyleOverlay ? 0.0f : 1.0f); +} + +CocoaScrollBar::~CocoaScrollBar() { + [bridge_ clearDelegate]; +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar, BaseScrollBar: + +gfx::Rect CocoaScrollBar::GetTrackBounds() const { + gfx::Rect local_bounds(GetLocalBounds()); + + int inset = kScrollbarThumbInset; + if (is_expanded_) { + inset = thickness_animation_.CurrentValueBetween( + kScrollbarThumbInset, kExpandedScrollbarThumbInset); + } + local_bounds.Inset(inset, inset); + + gfx::Size track_size = local_bounds.size(); + track_size.SetToMax(GetThumb()->GetPreferredSize()); + local_bounds.set_size(track_size); + return local_bounds; +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar, ScrollBar: + +int CocoaScrollBar::GetLayoutSize() const { + return scroller_style_ == NSScrollerStyleOverlay ? 0 : ScrollbarThickness(); +} + +int CocoaScrollBar::GetContentOverlapSize() const { + return scroller_style_ == NSScrollerStyleLegacy ? 0 : ScrollbarThickness(); +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar::View: + +void CocoaScrollBar::Layout() { + // Set the thickness of the thumb according to the track bounds. + // The length of the thumb is set by BaseScrollBar::Update(). + gfx::Rect thumb_bounds(GetThumb()->bounds()); + gfx::Rect track_bounds(GetTrackBounds()); + if (IsHorizontal()) { + GetThumb()->SetBounds(thumb_bounds.x(), + track_bounds.y(), + thumb_bounds.width(), + track_bounds.height()); + } else { + GetThumb()->SetBounds(track_bounds.x(), + thumb_bounds.y(), + track_bounds.width(), + thumb_bounds.height()); + } +} + +gfx::Size CocoaScrollBar::GetPreferredSize() const { + return gfx::Size(); +} + +void CocoaScrollBar::OnPaint(gfx::Canvas* canvas) { + if (!has_scrolltrack_) + return; + + // Paint the scrollbar track background. + gfx::Rect track_rect = GetLocalBounds(); + + SkPoint gradient_bounds[2]; + if (IsHorizontal()) { + gradient_bounds[0].set(track_rect.x(), track_rect.y()); + gradient_bounds[1].set(track_rect.x(), track_rect.bottom()); + } else { + gradient_bounds[0].set(track_rect.x(), track_rect.y()); + gradient_bounds[1].set(track_rect.right(), track_rect.y()); + } + SkPaint gradient; + gradient.setShader(SkGradientShader::MakeLinear( + gradient_bounds, kScrollerTrackGradientColors, nullptr, + arraysize(kScrollerTrackGradientColors), SkShader::kClamp_TileMode)); + canvas->DrawRect(track_rect, gradient); + + // Draw the inner border: top if horizontal, left if vertical. + SkPaint paint; + paint.setColor(kScrollerTrackInnerBorderColor); + gfx::Rect inner_border(track_rect); + if (IsHorizontal()) + inner_border.set_height(kScrollerTrackBorderWidth); + else + inner_border.set_width(kScrollerTrackBorderWidth); + canvas->DrawRect(inner_border, paint); + + // Draw the outer border: bottom if horizontal, right if veritcal. + paint.setColor(kScrollerTrackOuterBorderColor); + gfx::Rect outer_border(inner_border); + if (IsHorizontal()) + outer_border.set_y(track_rect.bottom()); + else + outer_border.set_x(track_rect.right()); + canvas->DrawRect(outer_border, paint); +} + +bool CocoaScrollBar::OnMousePressed(const ui::MouseEvent& event) { + // Ignore the mouse press if the scrollbar is hidden. + if (IsScrollbarFullyHidden()) + return false; + + return BaseScrollBar::OnMousePressed(event); +} + +void CocoaScrollBar::OnMouseReleased(const ui::MouseEvent& event) { + ResetOverlayScrollbar(); +} + +void CocoaScrollBar::OnMouseEntered(const ui::MouseEvent& event) { + if (scroller_style_ != NSScrollerStyleOverlay) + return; + + // If the scrollbar thumb did not completely fade away, then reshow it when + // the mouse enters the scrollbar thumb. + if (!IsScrollbarFullyHidden()) + ShowScrollbar(); + + // Expand the scrollbar. If the scrollbar is hidden, don't animate it. + if (!is_expanded_) { + SetScrolltrackVisible(true); + is_expanded_ = true; + if (IsScrollbarFullyHidden()) { + thickness_animation_.Reset(1.0); + UpdateScrollbarThickness(); + } else { + thickness_animation_.Show(); + } + } + + hide_scrollbar_timer_.Reset(); +} + +void CocoaScrollBar::OnMouseExited(const ui::MouseEvent& event) { + ResetOverlayScrollbar(); +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar::BaseScrollBar: + +void CocoaScrollBar::ScrollToPosition(int position) { + BaseScrollBar::ScrollToPosition(position); + + if (GetCocoaScrollBarThumb()->IsStatePressed()) + did_start_dragging_ = true; + + if (scroller_style_ == NSScrollerStyleOverlay) { + ShowScrollbar(); + hide_scrollbar_timer_.Reset(); + } +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar::ViewsScrollbarBridge: + +void CocoaScrollBar::OnScrollerStyleChanged() { + NSScrollerStyle scroller_style = + [ViewsScrollbarBridge getPreferredScrollerStyle]; + if (scroller_style_ == scroller_style) + return; + + // Cancel all of the animations. + thickness_animation_.Reset(); + layer()->GetAnimator()->AbortAllAnimations(); + + scroller_style_ = scroller_style; + + // Ensure that the ScrollView updates the scrollbar's layout. + if (parent()) + parent()->Layout(); + + if (scroller_style_ == NSScrollerStyleOverlay) { + // Hide the scrollbar, but don't fade out. + layer()->SetOpacity(0.0f); + ResetOverlayScrollbar(); + GetThumb()->SchedulePaint(); + } else { + is_expanded_ = false; + SetScrolltrackVisible(true); + ShowScrollbar(); + } +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar::ImplicitAnimationObserver: + +void CocoaScrollBar::OnImplicitAnimationsCompleted() { + DCHECK_EQ(scroller_style_, NSScrollerStyleOverlay); + ResetOverlayScrollbar(); +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar::AnimationDelegate: + +void CocoaScrollBar::AnimationProgressed(const gfx::Animation* animation) { + DCHECK(is_expanded_); + UpdateScrollbarThickness(); +} + +void CocoaScrollBar::AnimationEnded(const gfx::Animation* animation) { + // Remove the scrolltrack and set |is_expanded| to false at the end of + // the shrink animation. + if (!thickness_animation_.IsShowing()) { + is_expanded_ = false; + SetScrolltrackVisible(false); + } +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar, public: + +bool CocoaScrollBar::IsScrollbarFullyHidden() const { + return layer()->opacity() == 0.0f; +} + +////////////////////////////////////////////////////////////////// +// CocoaScrollBar, private: + +void CocoaScrollBar::HideScrollbar() { + DCHECK_EQ(scroller_style_, NSScrollerStyleOverlay); + + // Don't disappear if the scrollbar is hovered, or pressed but not dragged. + // This behavior matches the Cocoa scrollbars, but differs from the Blink + // scrollbars which would just disappear. + CocoaScrollBarThumb* thumb = GetCocoaScrollBarThumb(); + if (IsMouseHovered() || thumb->IsStateHovered() || + (thumb->IsStatePressed() && !did_start_dragging_)) { + hide_scrollbar_timer_.Reset(); + return; + } + + did_start_dragging_ = false; + + ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator()); + animation.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kFadeDurationMs)); + animation.AddObserver(this); + layer()->SetOpacity(0.0f); +} + +void CocoaScrollBar::ShowScrollbar() { + // If the scrollbar is still expanded but has not completely faded away, + // then shrink it back to its original state. + if (is_expanded_ && !IsHoverOrPressedState() && + layer()->GetAnimator()->IsAnimatingProperty( + ui::LayerAnimationElement::OPACITY)) { + DCHECK_EQ(scroller_style_, NSScrollerStyleOverlay); + thickness_animation_.Hide(); + } + + // Updates the scrolltrack and repaint it, if necessary. + double opacity = + scroller_style_ == NSScrollerStyleOverlay ? kOverlayOpacity : 1.0f; + layer()->SetOpacity(opacity); + hide_scrollbar_timer_.Stop(); +} + +bool CocoaScrollBar::IsHoverOrPressedState() const { + CocoaScrollBarThumb* thumb = GetCocoaScrollBarThumb(); + return thumb->IsStateHovered() || + thumb->IsStatePressed() || + IsMouseHovered(); +} + +void CocoaScrollBar::UpdateScrollbarThickness() { + int thickness = ScrollbarThickness(); + if (IsHorizontal()) + SetBounds(x(), bounds().bottom() - thickness, width(), thickness); + else + SetBounds(bounds().right() - thickness, y(), thickness, height()); +} + +void CocoaScrollBar::ResetOverlayScrollbar() { + if (!IsHoverOrPressedState() && IsScrollbarFullyHidden() && + !thickness_animation_.IsClosing()) { + if (is_expanded_) { + is_expanded_ = false; + thickness_animation_.Reset(); + UpdateScrollbarThickness(); + } + SetScrolltrackVisible(false); + } +} + +int CocoaScrollBar::ScrollbarThickness() const { + if (scroller_style_ == NSScrollerStyleLegacy) + return kScrollbarThickness; + + return thickness_animation_.CurrentValueBetween(kScrollbarThickness, + kExpandedScrollbarThickness); +} + +void CocoaScrollBar::SetScrolltrackVisible(bool visible) { + has_scrolltrack_ = visible; + SchedulePaint(); +} + +CocoaScrollBarThumb* CocoaScrollBar::GetCocoaScrollBarThumb() const { + return static_cast<CocoaScrollBarThumb*>(GetThumb()); +} + +} // namespace views diff --git a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.cc deleted file mode 100644 index 35c81615c48..00000000000 --- a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.cc +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/scrollbar/kennedy_scroll_bar.h" - -#include "base/macros.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkXfermode.h" -#include "ui/gfx/canvas.h" -#include "ui/views/background.h" -#include "ui/views/border.h" -#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" - -namespace views { -namespace { - -const int kScrollbarWidth = 10; -const int kThumbMinimumSize = kScrollbarWidth * 2; -const SkColor kBorderColor = SkColorSetARGB(32, 0, 0, 0); -const SkColor kThumbHoverColor = SkColorSetARGB(128, 0, 0, 0); -const SkColor kThumbDefaultColor = SkColorSetARGB(64, 0, 0, 0); -const SkColor kTrackHoverColor = SkColorSetARGB(32, 0, 0, 0); - -class KennedyScrollBarThumb : public BaseScrollBarThumb { - public: - explicit KennedyScrollBarThumb(BaseScrollBar* scroll_bar); - ~KennedyScrollBarThumb() override; - - protected: - // View overrides: - gfx::Size GetPreferredSize() const override; - void OnPaint(gfx::Canvas* canvas) override; - - private: - DISALLOW_COPY_AND_ASSIGN(KennedyScrollBarThumb); -}; - -KennedyScrollBarThumb::KennedyScrollBarThumb(BaseScrollBar* scroll_bar) - : BaseScrollBarThumb(scroll_bar) { -} - -KennedyScrollBarThumb::~KennedyScrollBarThumb() { -} - -gfx::Size KennedyScrollBarThumb::GetPreferredSize() const { - return gfx::Size(kThumbMinimumSize, kThumbMinimumSize); -} - -void KennedyScrollBarThumb::OnPaint(gfx::Canvas* canvas) { - gfx::Rect local_bounds(GetLocalBounds()); - canvas->FillRect(local_bounds, - (GetState() == CustomButton::STATE_HOVERED || - GetState() == CustomButton::STATE_PRESSED) ? - kThumbHoverColor : kThumbDefaultColor); - canvas->DrawRect(local_bounds, kBorderColor); -} - -} // namespace - -KennedyScrollBar::KennedyScrollBar(bool horizontal) - : BaseScrollBar(horizontal, new KennedyScrollBarThumb(this)) { - set_notify_enter_exit_on_child(true); -} - -KennedyScrollBar::~KennedyScrollBar() { -} - -gfx::Rect KennedyScrollBar::GetTrackBounds() const { - gfx::Rect local_bounds(GetLocalBounds()); - gfx::Size track_size = local_bounds.size(); - track_size.SetToMax(GetThumb()->size()); - local_bounds.set_size(track_size); - return local_bounds; -} - -int KennedyScrollBar::GetLayoutSize() const { - return kScrollbarWidth; -} - -gfx::Size KennedyScrollBar::GetPreferredSize() const { - return GetTrackBounds().size(); -} - -void KennedyScrollBar::Layout() { - gfx::Rect thumb_bounds = GetTrackBounds(); - BaseScrollBarThumb* thumb = GetThumb(); - if (IsHorizontal()) { - thumb_bounds.set_x(thumb->x()); - thumb_bounds.set_width(thumb->width()); - } else { - thumb_bounds.set_y(thumb->y()); - thumb_bounds.set_height(thumb->height()); - } - thumb->SetBoundsRect(thumb_bounds); -} - -void KennedyScrollBar::OnPaint(gfx::Canvas* canvas) { - CustomButton::ButtonState state = GetThumbTrackState(); - if ((state == CustomButton::STATE_HOVERED) || - (state == CustomButton::STATE_PRESSED)) { - gfx::Rect local_bounds(GetLocalBounds()); - canvas->FillRect(local_bounds, kTrackHoverColor); - canvas->DrawRect(local_bounds, kBorderColor); - } -} - -} // namespace views diff --git a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.h b/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.h deleted file mode 100644 index f56e4a91da4..00000000000 --- a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_CONTROLS_SCROLLBAR_KENNEDY_SCROLL_BAR_H_ -#define UI_VIEWS_CONTROLS_SCROLLBAR_KENNEDY_SCROLL_BAR_H_ - -#include "base/macros.h" -#include "ui/views/controls/scrollbar/base_scroll_bar.h" - -namespace views { - -// The scrollbar of kennedy style. Transparent track and grey rectangle -// thumb. Right now it doesn't have the way to share the background, -// so it will accept the background color instead. -class VIEWS_EXPORT KennedyScrollBar : public BaseScrollBar { - public: - explicit KennedyScrollBar(bool horizontal); - ~KennedyScrollBar() override; - - protected: - // BaseScrollBar overrides: - gfx::Rect GetTrackBounds() const override; - - // ScrollBar overrides: - int GetLayoutSize() const override; - - // View overrides: - gfx::Size GetPreferredSize() const override; - void Layout() override; - void OnPaint(gfx::Canvas* canvas) override; - - private: - DISALLOW_COPY_AND_ASSIGN(KennedyScrollBar); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_SCROLLBAR_KENNEDY_SCROLL_BAR_H_ diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc index fa72066818e..8da5f7d4290 100644 --- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc @@ -48,7 +48,7 @@ OverlayScrollBarThumb::OverlayScrollBarThumb(BaseScrollBar* scroll_bar) // This is necessary, otherwise the thumb will be rendered below the views if // those views paint to their own layers. SetPaintToLayer(true); - SetFillsBoundsOpaquely(false); + layer()->SetFillsBoundsOpaquely(false); } OverlayScrollBarThumb::~OverlayScrollBarThumb() { diff --git a/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc b/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc index 68a6687392f..84471929234 100644 --- a/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc +++ b/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc @@ -34,7 +34,7 @@ class TestScrollBarController : public views::ScrollBarController { return 10; } - // We save the last values in order to assert the corectness of the scroll + // We save the last values in order to assert the correctness of the scroll // operation. views::ScrollBar* last_source; bool last_is_positive; @@ -70,7 +70,7 @@ class NativeScrollBarTest : public ViewsTestBase { scrollbar_ = static_cast<NativeScrollBarViews*>(native_scrollbar_->native_wrapper_); scrollbar_->SetBounds(0, 0, 100, 100); - scrollbar_->Update(100, 200, 0); + scrollbar_->Update(100, 1000, 0); track_size_ = scrollbar_->GetTrackBounds().width(); } @@ -107,50 +107,58 @@ class NativeScrollBarTest : public ViewsTestBase { #endif TEST_F(NativeScrollBarTest, MAYBE_Scrolling) { - EXPECT_EQ(scrollbar_->GetPosition(), 0); - EXPECT_EQ(scrollbar_->GetMaxPosition(), 100); - EXPECT_EQ(scrollbar_->GetMinPosition(), 0); + EXPECT_EQ(0, scrollbar_->GetPosition()); + EXPECT_EQ(900, scrollbar_->GetMaxPosition()); + EXPECT_EQ(0, scrollbar_->GetMinPosition()); // Scroll to middle. - scrollbar_->ScrollToThumbPosition(track_size_ / 4, false); - EXPECT_EQ(controller_->last_position, 50); - EXPECT_EQ(controller_->last_source, native_scrollbar_); + scrollbar_->ScrollToThumbPosition(track_size_ / 2, true); + EXPECT_EQ(450, controller_->last_position); + EXPECT_EQ(native_scrollbar_, controller_->last_source); // Scroll to the end. - scrollbar_->ScrollToThumbPosition(track_size_ / 2, false); - EXPECT_EQ(controller_->last_position, 100); + scrollbar_->ScrollToThumbPosition(track_size_, true); + EXPECT_EQ(900, controller_->last_position); // Overscroll. Last position should be the maximum position. - scrollbar_->ScrollToThumbPosition(track_size_, false); - EXPECT_EQ(controller_->last_position, 100); + scrollbar_->ScrollToThumbPosition(track_size_ + 100, true); + EXPECT_EQ(900, controller_->last_position); // Underscroll. Last position should be the minimum position. scrollbar_->ScrollToThumbPosition(-10, false); - EXPECT_EQ(controller_->last_position, 0); + EXPECT_EQ(0, controller_->last_position); // Test the different fixed scrolling amounts. Generally used by buttons, // or click on track. scrollbar_->ScrollToThumbPosition(0, false); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_LINE); - EXPECT_EQ(controller_->last_position, 10); + EXPECT_EQ(10, controller_->last_position); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_PREV_LINE); - EXPECT_EQ(controller_->last_position, 0); + EXPECT_EQ(0, controller_->last_position); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_PAGE); - EXPECT_EQ(controller_->last_position, 20); + EXPECT_EQ(20, controller_->last_position); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_PREV_PAGE); - EXPECT_EQ(controller_->last_position, 0); + EXPECT_EQ(0, controller_->last_position); } TEST_F(NativeScrollBarTest, MAYBE_ScrollBarFitsToBottom) { - scrollbar_->Update(100, 199, 0); + scrollbar_->Update(100, 1999, 0); EXPECT_EQ(0, scrollbar_->GetPosition()); - EXPECT_EQ(99, scrollbar_->GetMaxPosition()); + EXPECT_EQ(1899, scrollbar_->GetMaxPosition()); EXPECT_EQ(0, scrollbar_->GetMinPosition()); - scrollbar_->Update(100, 199, 99); + // Scroll to the midpoint of the document. + scrollbar_->Update(100, 1999, 950); + EXPECT_EQ((scrollbar_->GetTrackBounds().width() - + scrollbar_->GetThumbSizeForTest()) / + 2, + scrollbar_->GetPosition()); + + // Scroll to the end of the document. + scrollbar_->Update(100, 1999, 1899); EXPECT_EQ( scrollbar_->GetTrackBounds().width() - scrollbar_->GetThumbSizeForTest(), scrollbar_->GetPosition()); @@ -158,13 +166,37 @@ TEST_F(NativeScrollBarTest, MAYBE_ScrollBarFitsToBottom) { TEST_F(NativeScrollBarTest, ScrollToEndAfterShrinkAndExpand) { // Scroll to the end of the content. - scrollbar_->Update(100, 101, 0); + scrollbar_->Update(100, 1001, 0); EXPECT_TRUE(scrollbar_->ScrollByContentsOffset(-1)); - // Shrink and then re-exapnd the content. - scrollbar_->Update(100, 100, 0); - scrollbar_->Update(100, 101, 0); + // Shrink and then re-expand the content. + scrollbar_->Update(100, 1000, 0); + scrollbar_->Update(100, 1001, 0); // Ensure the scrollbar allows scrolling to the end. EXPECT_TRUE(scrollbar_->ScrollByContentsOffset(-1)); } +TEST_F(NativeScrollBarTest, ThumbFullLengthOfTrack) { + // Shrink content so that it fits within the viewport. + scrollbar_->Update(100, 10, 0); + EXPECT_EQ(scrollbar_->GetTrackBounds().width(), + scrollbar_->GetThumbSizeForTest()); + // Emulate a click on the full size scroll bar. + scrollbar_->ScrollToThumbPosition(0, false); + EXPECT_EQ(0, scrollbar_->GetPosition()); + // Emulate a key down. + scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_LINE); + EXPECT_EQ(0, scrollbar_->GetPosition()); + + // Expand content so that it fits *exactly* within the viewport. + scrollbar_->Update(100, 100, 0); + EXPECT_EQ(scrollbar_->GetTrackBounds().width(), + scrollbar_->GetThumbSizeForTest()); + // Emulate a click on the full size scroll bar. + scrollbar_->ScrollToThumbPosition(0, false); + EXPECT_EQ(0, scrollbar_->GetPosition()); + // Emulate a key down. + scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_LINE); + EXPECT_EQ(0, scrollbar_->GetPosition()); +} + } // namespace views diff --git a/chromium/ui/views/controls/single_split_view.cc b/chromium/ui/views/controls/single_split_view.cc index c7556916962..354d3e14bb9 100644 --- a/chromium/ui/views/controls/single_split_view.cc +++ b/chromium/ui/views/controls/single_split_view.cc @@ -9,6 +9,7 @@ #include "ui/base/cursor/cursor.h" #include "ui/gfx/canvas.h" #include "ui/views/background.h" +#include "ui/views/border.h" #include "ui/views/controls/single_split_view_listener.h" #include "ui/views/native_cursor.h" @@ -45,7 +46,8 @@ SingleSplitView::SingleSplitView(View* leading, void SingleSplitView::Layout() { gfx::Rect leading_bounds; gfx::Rect trailing_bounds; - CalculateChildrenBounds(bounds(), &leading_bounds, &trailing_bounds); + CalculateChildrenBounds(GetContentsBounds(), &leading_bounds, + &trailing_bounds); if (has_children()) { if (child_at(0)->visible()) @@ -87,6 +89,8 @@ gfx::Size SingleSplitView::GetPreferredSize() const { width += GetDividerSize(); else height += GetDividerSize(); + width += GetInsets().width(); + height += GetInsets().height(); return gfx::Size(width, height); } @@ -123,24 +127,25 @@ void SingleSplitView::CalculateChildrenBounds( } else if (!is_leading_visible) { divider_at = 0; } else { - divider_at = - CalculateDividerOffset(divider_offset_, this->bounds(), bounds); + divider_at = CalculateDividerOffset(divider_offset_, bounds, bounds); divider_at = NormalizeDividerOffset(divider_at, bounds); } int divider_size = GetDividerSize(); if (is_horizontal_) { - *leading_bounds = gfx::Rect(0, 0, divider_at, bounds.height()); + *leading_bounds = + gfx::Rect(bounds.x(), bounds.y(), divider_at, bounds.height()); *trailing_bounds = - gfx::Rect(divider_at + divider_size, 0, + gfx::Rect(divider_at + divider_size + bounds.x(), bounds.y(), std::max(0, bounds.width() - divider_at - divider_size), bounds.height()); } else { - *leading_bounds = gfx::Rect(0, 0, bounds.width(), divider_at); - *trailing_bounds = - gfx::Rect(0, divider_at + divider_size, bounds.width(), - std::max(0, bounds.height() - divider_at - divider_size)); + *leading_bounds = + gfx::Rect(bounds.x(), bounds.y(), bounds.width(), divider_at); + *trailing_bounds = gfx::Rect( + bounds.x(), divider_at + divider_size + bounds.y(), bounds.width(), + std::max(0, bounds.height() - divider_at - divider_size)); } } @@ -153,7 +158,7 @@ bool SingleSplitView::OnMousePressed(const ui::MouseEvent& event) { return false; drag_info_.initial_mouse_offset = GetPrimaryAxisSize(event.x(), event.y()); drag_info_.initial_divider_offset = - NormalizeDividerOffset(divider_offset_, bounds()); + NormalizeDividerOffset(divider_offset_, GetContentsBounds()); return true; } @@ -196,8 +201,10 @@ void SingleSplitView::OnMouseCaptureLost() { } void SingleSplitView::OnBoundsChanged(const gfx::Rect& previous_bounds) { - divider_offset_ = CalculateDividerOffset(divider_offset_, previous_bounds, - bounds()); + gfx::Rect previous_content_bounds = previous_bounds; + previous_content_bounds.Inset(GetInsets()); + divider_offset_ = CalculateDividerOffset( + divider_offset_, previous_content_bounds, GetContentsBounds()); } bool SingleSplitView::IsPointInDivider(const gfx::Point& p) { diff --git a/chromium/ui/views/controls/single_split_view_unittest.cc b/chromium/ui/views/controls/single_split_view_unittest.cc index 3f3e6a086e6..d2a588e0c74 100644 --- a/chromium/ui/views/controls/single_split_view_unittest.cc +++ b/chromium/ui/views/controls/single_split_view_unittest.cc @@ -10,6 +10,7 @@ #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event_utils.h" +#include "ui/views/border.h" #include "ui/views/controls/single_split_view_listener.h" namespace { @@ -31,15 +32,19 @@ static void VerifySplitViewLayout(const views::SingleSplitView& split) { EXPECT_FALSE(leading->bounds().Intersects(trailing->bounds())); if (split.orientation() == views::SingleSplitView::HORIZONTAL_SPLIT) { - EXPECT_EQ(leading->bounds().height(), split.bounds().height()); - EXPECT_EQ(trailing->bounds().height(), split.bounds().height()); + EXPECT_EQ(leading->bounds().height(), + split.bounds().height() - split.GetInsets().height()); + EXPECT_EQ(trailing->bounds().height(), + split.bounds().height() - split.GetInsets().height()); EXPECT_LT(leading->bounds().width() + trailing->bounds().width(), - split.bounds().width()); + split.bounds().width() - split.GetInsets().width()); } else if (split.orientation() == views::SingleSplitView::VERTICAL_SPLIT) { - EXPECT_EQ(leading->bounds().width(), split.bounds().width()); - EXPECT_EQ(trailing->bounds().width(), split.bounds().width()); + EXPECT_EQ(leading->bounds().width(), + split.bounds().width() - split.GetInsets().width()); + EXPECT_EQ(trailing->bounds().width(), + split.bounds().width() - split.GetInsets().width()); EXPECT_LT(leading->bounds().height() + trailing->bounds().height(), - split.bounds().height()); + split.bounds().height() - split.GetInsets().height()); } else { NOTREACHED(); } @@ -109,47 +114,59 @@ TEST(SingleSplitViewTest, Resize) { SingleSplitView::VERTICAL_SPLIT }; + int borders[][4] = { + {0, 0, 0, 0}, {5, 5, 5, 5}, {10, 5, 5, 0}, + }; + for (size_t orientation = 0; orientation < arraysize(orientations); ++orientation) { - // Create a split view. - SingleSplitView split( - new View(), new View(), orientations[orientation], NULL); - - // Set initial size and divider offset. - EXPECT_EQ(test_cases[0].primary_axis_size, - test_cases[0].secondary_axis_size); - split.SetBounds(0, 0, test_cases[0].primary_axis_size, - test_cases[0].secondary_axis_size); - split.set_divider_offset(test_cases[0].divider_offset); - split.Layout(); - - // Run all test cases. - for (size_t i = 0; i < arraysize(test_cases); ++i) { - split.set_resize_leading_on_bounds_change( - test_cases[i].resize_leading_on_bounds_change); - if (split.orientation() == SingleSplitView::HORIZONTAL_SPLIT) { - split.SetBounds(0, 0, test_cases[i].primary_axis_size, - test_cases[i].secondary_axis_size); - } else { - split.SetBounds(0, 0, test_cases[i].secondary_axis_size, - test_cases[i].primary_axis_size); + for (size_t border = 0; border < arraysize(borders); ++border) { + // Create a split view. + SingleSplitView split(new View(), new View(), orientations[orientation], + NULL); + + // Set initial size and divider offset. + EXPECT_EQ(test_cases[0].primary_axis_size, + test_cases[0].secondary_axis_size); + split.SetBounds(0, 0, test_cases[0].primary_axis_size, + test_cases[0].secondary_axis_size); + + if (border != 0) + split.SetBorder( + Border::CreateEmptyBorder(borders[border][0], borders[border][1], + borders[border][2], borders[border][3])); + + split.set_divider_offset(test_cases[0].divider_offset); + split.Layout(); + + // Run all test cases. + for (size_t i = 0; i < arraysize(test_cases); ++i) { + split.set_resize_leading_on_bounds_change( + test_cases[i].resize_leading_on_bounds_change); + if (split.orientation() == SingleSplitView::HORIZONTAL_SPLIT) { + split.SetBounds(0, 0, test_cases[i].primary_axis_size, + test_cases[i].secondary_axis_size); + } else { + split.SetBounds(0, 0, test_cases[i].secondary_axis_size, + test_cases[i].primary_axis_size); + } + + EXPECT_EQ(test_cases[i].divider_offset, split.divider_offset()); + VerifySplitViewLayout(split); } - EXPECT_EQ(test_cases[i].divider_offset, split.divider_offset()); - VerifySplitViewLayout(split); - } + // Special cases, one of the child views is hidden. + split.child_at(0)->SetVisible(false); + split.Layout(); - // Special cases, one of the child views is hidden. - split.child_at(0)->SetVisible(false); - split.Layout(); + EXPECT_EQ(split.GetContentsBounds().size(), split.child_at(1)->size()); - EXPECT_EQ(split.size(), split.child_at(1)->size()); + split.child_at(0)->SetVisible(true); + split.child_at(1)->SetVisible(false); + split.Layout(); - split.child_at(0)->SetVisible(true); - split.child_at(1)->SetVisible(false); - split.Layout(); - - EXPECT_EQ(split.size(), split.child_at(0)->size()); + EXPECT_EQ(split.GetContentsBounds().size(), split.child_at(0)->size()); + } } } diff --git a/chromium/ui/views/controls/slide_out_view.cc b/chromium/ui/views/controls/slide_out_view.cc index d3cafecf7eb..2775c0889af 100644 --- a/chromium/ui/views/controls/slide_out_view.cc +++ b/chromium/ui/views/controls/slide_out_view.cc @@ -10,23 +10,25 @@ namespace views { -SlideOutView::SlideOutView() - : gesture_scroll_amount_(0.f) { +SlideOutView::SlideOutView() { // If accelerated compositing is not available, this widget tracks the // OnSlideOut event but does not render any visible changes. SetPaintToLayer(true); - SetFillsBoundsOpaquely(false); + layer()->SetFillsBoundsOpaquely(false); } SlideOutView::~SlideOutView() { } void SlideOutView::OnGestureEvent(ui::GestureEvent* event) { + const float kScrollRatioForClosingNotification = 0.5f; + if (event->type() == ui::ET_SCROLL_FLING_START) { // The threshold for the fling velocity is computed empirically. // The unit is in pixels/second. const float kFlingThresholdForClose = 800.f; - if (fabsf(event->details().velocity_x()) > kFlingThresholdForClose) { + if (is_slide_out_enabled_ && + fabsf(event->details().velocity_x()) > kFlingThresholdForClose) { SlideOutAndClose(event->details().velocity_x() < 0 ? SLIDE_LEFT : SLIDE_RIGHT); event->StopPropagation(); @@ -40,22 +42,35 @@ void SlideOutView::OnGestureEvent(ui::GestureEvent* event) { return; if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) { - gesture_scroll_amount_ = 0.f; + gesture_amount_ = 0.f; } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { // The scroll-update events include the incremental scroll amount. - gesture_scroll_amount_ += event->details().scroll_x(); + gesture_amount_ += event->details().scroll_x(); + + float scroll_amount; + if (is_slide_out_enabled_) { + scroll_amount = gesture_amount_; + layer()->SetOpacity(1.f - std::min(fabsf(scroll_amount) / width(), 1.f)); + } else { + if (gesture_amount_ >= 0) { + scroll_amount = std::min(0.5f * gesture_amount_, + width() * kScrollRatioForClosingNotification); + } else { + scroll_amount = + std::max(0.5f * gesture_amount_, + -1.f * width() * kScrollRatioForClosingNotification); + } + } gfx::Transform transform; - transform.Translate(gesture_scroll_amount_, 0.0); + transform.Translate(scroll_amount, 0.0); layer()->SetTransform(transform); - layer()->SetOpacity( - 1.f - std::min(fabsf(gesture_scroll_amount_) / width(), 1.f)); } else if (event->type() == ui::ET_GESTURE_SCROLL_END) { - const float kScrollRatioForClosingNotification = 0.5f; - float scrolled_ratio = fabsf(gesture_scroll_amount_) / width(); - if (scrolled_ratio >= kScrollRatioForClosingNotification) { - SlideOutAndClose(gesture_scroll_amount_ < 0 ? SLIDE_LEFT : SLIDE_RIGHT); + float scrolled_ratio = fabsf(gesture_amount_) / width(); + if (is_slide_out_enabled_ && + scrolled_ratio >= kScrollRatioForClosingNotification) { + SlideOutAndClose(gesture_amount_ < 0 ? SLIDE_LEFT : SLIDE_RIGHT); event->StopPropagation(); return; } diff --git a/chromium/ui/views/controls/slide_out_view.h b/chromium/ui/views/controls/slide_out_view.h index 7fdb637cc7b..89962f2d317 100644 --- a/chromium/ui/views/controls/slide_out_view.h +++ b/chromium/ui/views/controls/slide_out_view.h @@ -19,6 +19,8 @@ class VIEWS_EXPORT SlideOutView : public views::View, SlideOutView(); ~SlideOutView() override; + bool slide_out_enabled() { return is_slide_out_enabled_; } + protected: // Called when user intends to close the View by sliding it out. virtual void OnSlideOut() = 0; @@ -26,6 +28,10 @@ class VIEWS_EXPORT SlideOutView : public views::View, // Overridden from views::View. void OnGestureEvent(ui::GestureEvent* event) override; + void set_slide_out_enabled(bool is_slide_out_enabled) { + is_slide_out_enabled_ = is_slide_out_enabled; + } + private: enum SlideDirection { SLIDE_LEFT, @@ -41,7 +47,8 @@ class VIEWS_EXPORT SlideOutView : public views::View, // Overridden from ImplicitAnimationObserver. void OnImplicitAnimationsCompleted() override; - float gesture_scroll_amount_; + float gesture_amount_ = 0.f; + bool is_slide_out_enabled_ = true; DISALLOW_COPY_AND_ASSIGN(SlideOutView); }; diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc index 3909e7f021b..9c8f720cee1 100644 --- a/chromium/ui/views/controls/styled_label.cc +++ b/chromium/ui/views/controls/styled_label.cc @@ -99,7 +99,8 @@ const char StyledLabel::kViewClassName[] = "StyledLabel"; StyledLabel::StyledLabel(const base::string16& text, StyledLabelListener* listener) - : specified_line_height_(0), + : font_list_(Label().font_list()), + specified_line_height_(0), listener_(listener), width_at_last_size_calculation_(0), width_at_last_layout_(0), diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc index 3c0a7e741f0..cae7f67744e 100644 --- a/chromium/ui/views/controls/styled_label_unittest.cc +++ b/chromium/ui/views/controls/styled_label_unittest.cc @@ -184,7 +184,8 @@ TEST_F(StyledLabelTest, DontBreakLinks) { const std::string link_text("and this should be a link"); InitStyledLabel(text + link_text); styled()->AddStyleRange( - gfx::Range(text.size(), text.size() + link_text.size()), + gfx::Range(static_cast<uint32_t>(text.size()), + static_cast<uint32_t>(text.size() + link_text.size())), StyledLabel::RangeStyleInfo::CreateForLink()); Label label(ASCIIToUTF16(text + link_text.substr(0, link_text.size() / 2))); @@ -209,7 +210,8 @@ TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) { StyledLabel::RangeStyleInfo style_info; style_info.disable_line_wrapping = true; styled()->AddStyleRange( - gfx::Range(text.size(), text.size() + unbreakable_text.size()), + gfx::Range(static_cast<uint32_t>(text.size()), + static_cast<uint32_t>(text.size() + unbreakable_text.size())), style_info); Label label(ASCIIToUTF16( @@ -233,7 +235,8 @@ TEST_F(StyledLabelTest, StyledRangeUnderlined) { StyledLabel::RangeStyleInfo style_info; style_info.font_style = gfx::Font::UNDERLINE; styled()->AddStyleRange( - gfx::Range(text.size(), text.size() + underlined_text.size()), + gfx::Range(static_cast<uint32_t>(text.size()), + static_cast<uint32_t>(text.size() + underlined_text.size())), style_info); styled()->SetBounds(0, 0, 1000, 1000); @@ -255,7 +258,8 @@ TEST_F(StyledLabelTest, StyledRangeBold) { StyledLabel::RangeStyleInfo style_info; style_info.font_style = gfx::Font::BOLD; - styled()->AddStyleRange(gfx::Range(0, bold_text.size()), style_info); + styled()->AddStyleRange( + gfx::Range(0u, static_cast<uint32_t>(bold_text.size())), style_info); // Calculate the bold text width if it were a pure label view, both with bold // and normal style. @@ -315,13 +319,15 @@ TEST_F(StyledLabelTest, Color) { StyledLabel::RangeStyleInfo style_info_red; style_info_red.color = SK_ColorRED; - styled()->AddStyleRange(gfx::Range(0, text_red.size()), style_info_red); + styled()->AddStyleRange( + gfx::Range(0u, static_cast<uint32_t>(text_red.size())), style_info_red); StyledLabel::RangeStyleInfo style_info_link = StyledLabel::RangeStyleInfo::CreateForLink(); - styled()->AddStyleRange(gfx::Range(text_red.size(), - text_red.size() + text_link.size()), - style_info_link); + styled()->AddStyleRange( + gfx::Range(static_cast<uint32_t>(text_red.size()), + static_cast<uint32_t>(text_red.size() + text_link.size())), + style_info_link); styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); @@ -377,10 +383,13 @@ TEST_F(StyledLabelTest, StyledRangeWithTooltip) { StyledLabel::RangeStyleInfo tooltip_style; tooltip_style.tooltip = ASCIIToUTF16("tooltip"); styled()->AddStyleRange( - gfx::Range(tooltip_start, tooltip_start + tooltip_text.size()), + gfx::Range(static_cast<uint32_t>(tooltip_start), + static_cast<uint32_t>(tooltip_start + tooltip_text.size())), tooltip_style); - styled()->AddStyleRange(gfx::Range(link_start, link_start + link_text.size()), - StyledLabel::RangeStyleInfo::CreateForLink()); + styled()->AddStyleRange( + gfx::Range(static_cast<uint32_t>(link_start), + static_cast<uint32_t>(link_start + link_text.size())), + StyledLabel::RangeStyleInfo::CreateForLink()); // Break line inside the range with the tooltip. Label label(ASCIIToUTF16( diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc index eb15ac9a3be..5579bb07c04 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -9,6 +9,7 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" #include "ui/accessibility/ax_view_state.h" +#include "ui/base/default_style.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" @@ -27,6 +28,10 @@ const SkColor kTabTitleColor_Hovered = SK_ColorBLACK; const SkColor kTabBorderColor = SkColorSetRGB(0xC8, 0xC8, 0xC8); const SkScalar kTabBorderThickness = 1.0f; +const gfx::Font::FontStyle kHoverStyle = gfx::Font::NORMAL; +const gfx::Font::FontStyle kActiveStyle = gfx::Font::BOLD; +const gfx::Font::FontStyle kInactiveStyle = gfx::Font::NORMAL; + } // namespace namespace views { @@ -102,9 +107,11 @@ const char Tab::kViewClassName[] = "Tab"; Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) : tabbed_pane_(tabbed_pane), - title_(new Label(title, - ui::ResourceBundle::GetSharedInstance().GetFontList( - ui::ResourceBundle::BoldFont))), + title_(new Label( + title, + ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta( + ui::kLabelFontSizeDelta, + kActiveStyle))), tab_state_(TAB_ACTIVE), contents_(contents) { // Calculate this now while the font list is guaranteed to be bold. @@ -182,15 +189,18 @@ void Tab::SetState(TabState tab_state) { switch (tab_state) { case TAB_INACTIVE: title_->SetEnabledColor(kTabTitleColor_Inactive); - title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BaseFont)); + title_->SetFontList( + rb.GetFontListWithDelta(ui::kLabelFontSizeDelta, kInactiveStyle)); break; case TAB_ACTIVE: title_->SetEnabledColor(kTabTitleColor_Active); - title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BoldFont)); + title_->SetFontList( + rb.GetFontListWithDelta(ui::kLabelFontSizeDelta, kActiveStyle)); break; case TAB_HOVERED: title_->SetEnabledColor(kTabTitleColor_Hovered); - title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BaseFont)); + title_->SetFontList( + rb.GetFontListWithDelta(ui::kLabelFontSizeDelta, kHoverStyle)); break; } SchedulePaint(); diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index a115af7d033..74d18b70564 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -12,9 +12,11 @@ #include "ui/accessibility/ax_view_state.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/cursor/cursor.h" +#include "ui/base/default_style.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_utils.h" #include "ui/base/ime/input_method.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/base/touch/selection_bound.h" #include "ui/base/ui_base_switches_util.h" #include "ui/compositor/canvas_painter.h" @@ -239,6 +241,11 @@ int GetViewsCommand(const ui::TextEditCommandAuraLinux& command, bool rtl) { } #endif +const gfx::FontList& GetDefaultFontList() { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); +} + } // namespace // static @@ -284,6 +291,7 @@ Textfield::Textfield() weak_ptr_factory_(this) { set_context_menu_controller(this); set_drag_controller(this); + GetRenderText()->SetFontList(GetDefaultFontList()); SetBorder(scoped_ptr<Border>(new FocusableBorder())); SetFocusable(true); @@ -1090,8 +1098,8 @@ void Textfield::WriteDragDataForView(View* sender, label.SetSubpixelRenderingEnabled(false); gfx::Size size(label.GetPreferredSize()); gfx::NativeView native_view = GetWidget()->GetNativeView(); - gfx::Display display = gfx::Screen::GetScreenFor(native_view)-> - GetDisplayNearestWindow(native_view); + gfx::Display display = + gfx::Screen::GetScreen()->GetDisplayNearestWindow(native_view); size.SetToMin(gfx::Size(display.size().width(), height())); label.SetBoundsRect(gfx::Rect(size)); scoped_ptr<gfx::Canvas> canvas( diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index dd00e40fd86..97595c57cb4 100644 --- a/chromium/ui/views/controls/textfield/textfield_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -436,11 +436,6 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { widget_->Show(); textfield_->RequestFocus(); - // On Mac, activation is asynchronous since desktop widgets are used. We - // don't want parallel tests to steal active status either, so fake it. -#if defined(OS_MACOSX) && !defined(USE_AURA) - fake_activation_ = test::WidgetTest::FakeWidgetIsActiveAlways(); -#endif event_generator_.reset( new ui::test::EventGenerator(GetContext(), widget_->GetNativeWindow())); } @@ -660,7 +655,6 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { private: ui::ClipboardType copied_to_clipboard_; - scoped_ptr<test::WidgetTest::FakeActivation> fake_activation_; scoped_ptr<ui::test::EventGenerator> event_generator_; DISALLOW_COPY_AND_ASSIGN(TextfieldTest); }; @@ -726,6 +720,10 @@ TEST_F(TextfieldTest, KeysWithModifiersTest) { SendKeyPress(ui::VKEY_2, command); SendKeyPress(ui::VKEY_3, 0); SendKeyPress(ui::VKEY_4, 0); + SendKeyPress(ui::VKEY_OEM_PLUS, ctrl); + SendKeyPress(ui::VKEY_OEM_PLUS, ctrl | shift); + SendKeyPress(ui::VKEY_OEM_MINUS, ctrl); + SendKeyPress(ui::VKEY_OEM_MINUS, ctrl | shift); if (TestingNativeCrOs()) EXPECT_STR_EQ("TeTEx34", textfield_->text()); diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc index b7918430e50..30428a31cad 100644 --- a/chromium/ui/views/controls/tree/tree_view.cc +++ b/chromium/ui/views/controls/tree/tree_view.cc @@ -250,9 +250,8 @@ void TreeView::SetSelectedNode(TreeModelNode* model_node) { controller_->OnTreeViewSelectionChanged(this); if (changed) { - // TODO(dmazzoni): Decide if EVENT_SELECTION_CHANGED is a better choice for - // sub-item selection event. - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); + NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true); + NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); } } diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc index 9a129c04611..686eeb6ddd4 100644 --- a/chromium/ui/views/controls/webview/webview.cc +++ b/chromium/ui/views/controls/webview/webview.cc @@ -294,7 +294,8 @@ void WebView::DidDestroyFullscreenWidget(int routing_id) { ReattachForFullscreenChange(false); } -void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) { +void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen, + bool will_cause_resize) { if (embed_fullscreen_widget_mode_enabled_) ReattachForFullscreenChange(entered_fullscreen); } @@ -338,24 +339,12 @@ void WebView::AttachWebContents() { if (focus_manager && focus_manager->GetFocusedView() == this) OnFocus(); -#if defined(OS_WIN) - if (!is_embedding_fullscreen_widget_) { - web_contents()->SetParentNativeViewAccessible( - parent()->GetNativeViewAccessible()); - } -#endif - OnWebContentsAttached(); } void WebView::DetachWebContents() { - if (web_contents()) { + if (web_contents()) holder_->Detach(); -#if defined(OS_WIN) - if (!is_embedding_fullscreen_widget_) - web_contents()->SetParentNativeViewAccessible(NULL); -#endif - } } void WebView::ReattachForFullscreenChange(bool enter_fullscreen) { diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h index cb8ba7a2b3b..9ff7c7f6091 100644 --- a/chromium/ui/views/controls/webview/webview.h +++ b/chromium/ui/views/controls/webview/webview.h @@ -133,7 +133,8 @@ class WEBVIEW_EXPORT WebView : public View, void WebContentsDestroyed() override; void DidShowFullscreenWidget(int routing_id) override; void DidDestroyFullscreenWidget(int routing_id) override; - void DidToggleFullscreenModeForTab(bool entered_fullscreen) override; + void DidToggleFullscreenModeForTab(bool entered_fullscreen, + bool will_cause_resize) override; void DidAttachInterstitialPage() override; void DidDetachInterstitialPage() override; // Workaround for MSVC++ linker bug/feature that requires diff --git a/chromium/ui/views/controls/webview/webview_unittest.cc b/chromium/ui/views/controls/webview/webview_unittest.cc index bc2a927c5a7..2ecf9578118 100644 --- a/chromium/ui/views/controls/webview/webview_unittest.cc +++ b/chromium/ui/views/controls/webview/webview_unittest.cc @@ -14,13 +14,16 @@ #include "content/public/test/test_browser_thread.h" #include "content/public/test/web_contents_tester.h" #include "content/test/test_content_browser_client.h" -#include "ui/aura/window.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/test/test_views_delegate.h" #include "ui/views/test/widget_test.h" +#if defined(USE_AURA) +#include "ui/aura/window.h" +#endif + namespace views { namespace { @@ -43,7 +46,7 @@ class WebViewTestViewsDelegate : public views::TestViewsDelegate { DISALLOW_COPY_AND_ASSIGN(WebViewTestViewsDelegate); }; -// Provides functionaity to observe events on a WebContents like WasShown/ +// Provides functionality to observe events on a WebContents like WasShown/ // WasHidden/WebContentsDestroyed. class WebViewTestWebContentsObserver : public content::WebContentsObserver { public: @@ -51,7 +54,8 @@ class WebViewTestWebContentsObserver : public content::WebContentsObserver { : web_contents_(web_contents), was_shown_(false), shown_count_(0), - hidden_count_(0) { + hidden_count_(0), + valid_root_while_shown_(true) { content::WebContentsObserver::Observe(web_contents); } @@ -67,8 +71,10 @@ class WebViewTestWebContentsObserver : public content::WebContentsObserver { } void WasShown() override { +#if defined(USE_AURA) valid_root_while_shown_ = web_contents()->GetNativeView()->GetRootWindow() != NULL; +#endif was_shown_ = true; ++shown_count_; } @@ -197,7 +203,9 @@ TEST_F(WebViewUnitTest, TestWebViewAttachDetachWebContents) { web_view()->SetWebContents(web_contents1.get()); EXPECT_TRUE(observer1.was_shown()); +#if defined(USE_AURA) EXPECT_TRUE(web_contents1->GetNativeView()->IsVisible()); +#endif EXPECT_EQ(observer1.shown_count(), 1); EXPECT_EQ(observer1.hidden_count(), 0); EXPECT_TRUE(observer1.valid_root_while_shown()); @@ -235,6 +243,7 @@ TEST_F(WebViewUnitTest, TestWebViewAttachDetachWebContents) { EXPECT_EQ(1, observer2.shown_count()); EXPECT_EQ(1, observer2.hidden_count()); +#if defined(USE_AURA) // Case 4: Test that making the webview visible when a window has an invisible // parent does not make the web contents visible. top_level_widget()->Hide(); @@ -246,7 +255,18 @@ TEST_F(WebViewUnitTest, TestWebViewAttachDetachWebContents) { EXPECT_EQ(2, observer1.shown_count()); top_level_widget()->Hide(); EXPECT_EQ(2, observer1.hidden_count()); - +#else + // On Mac, changes to window visibility do not trigger calls to WebContents:: + // WasShown() or WasHidden(), since the OS does not provide good signals for + // window visibility. However, we can still test that moving a visible WebView + // whose WebContents is not currently showing to a new, visible window will + // show the WebContents. Simulate the "hide window with visible WebView" step + // simply by detaching the WebContents. + web_view()->SetVisible(true); + EXPECT_EQ(2, observer1.shown_count()); + web_view()->holder()->Detach(); + EXPECT_EQ(2, observer1.hidden_count()); +#endif // Case 5: Test that moving from a hidden parent to a visible parent makes the // web contents visible. Widget* parent2 = CreateTopLevelFramelessPlatformWidget(); @@ -281,13 +301,13 @@ TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_Layout) { // WebView like before. delegate.set_is_fullscreened(true); static_cast<content::WebContentsObserver*>(web_view())-> - DidToggleFullscreenModeForTab(true); + DidToggleFullscreenModeForTab(true, false); EXPECT_EQ(gfx::Rect(0, 0, 100, 100), holder()->bounds()); // ...and transition back out of fullscreen mode. delegate.set_is_fullscreened(false); static_cast<content::WebContentsObserver*>(web_view())-> - DidToggleFullscreenModeForTab(false); + DidToggleFullscreenModeForTab(false, false); EXPECT_EQ(gfx::Rect(0, 0, 100, 100), holder()->bounds()); // Now, begin screen capture of the WebContents and then enter fullscreen @@ -297,7 +317,7 @@ TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_Layout) { web_contents->IncrementCapturerCount(capture_size); delegate.set_is_fullscreened(true); static_cast<content::WebContentsObserver*>(web_view())-> - DidToggleFullscreenModeForTab(true); + DidToggleFullscreenModeForTab(true, false); EXPECT_EQ(gfx::Rect(18, 26, 64, 48), holder()->bounds()); // Resize the WebView so that its width is smaller than the capture width. @@ -309,7 +329,7 @@ TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_Layout) { // of the holder fill the entire WebView once again. delegate.set_is_fullscreened(false); static_cast<content::WebContentsObserver*>(web_view())-> - DidToggleFullscreenModeForTab(false); + DidToggleFullscreenModeForTab(false, false); EXPECT_EQ(gfx::Rect(0, 0, 32, 32), holder()->bounds()); } @@ -340,7 +360,7 @@ TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_Switching) { web_contents1->IncrementCapturerCount(capture_size); delegate1.set_is_fullscreened(true); static_cast<content::WebContentsObserver*>(web_view())-> - DidToggleFullscreenModeForTab(true); + DidToggleFullscreenModeForTab(true, false); EXPECT_EQ(web_contents1->GetNativeView(), holder()->native_view()); EXPECT_EQ(gfx::Rect(18, 26, 64, 48), holder()->bounds()); @@ -393,7 +413,7 @@ TEST_F(WebViewUnitTest, EmbeddedFullscreenDuringScreenCapture_ClickToFocus) { web_contents->IncrementCapturerCount(capture_size); delegate.set_is_fullscreened(true); static_cast<content::WebContentsObserver*>(web_view())-> - DidToggleFullscreenModeForTab(true); + DidToggleFullscreenModeForTab(true, false); EXPECT_EQ(gfx::Rect(18, 21, 64, 48), holder()->bounds()); // Focus the other widget. diff --git a/chromium/ui/views/corewm/cursor_height_provider_win.cc b/chromium/ui/views/corewm/cursor_height_provider_win.cc index 17a6f83de91..398b009b71b 100644 --- a/chromium/ui/views/corewm/cursor_height_provider_win.cc +++ b/chromium/ui/views/corewm/cursor_height_provider_win.cc @@ -35,7 +35,7 @@ PixelData GetBitmapData(HBITMAP handle, const BITMAPINFO& info, HDC hdc) { // Masks are monochromatic. DCHECK_EQ(info.bmiHeader.biBitCount, 1); if (info.bmiHeader.biBitCount != 1) - return data.Pass(); + return data; // When getting pixel data palette is appended to memory pointed by // BITMAPINFO passed so allocate additional memory to store additional data. @@ -55,7 +55,7 @@ PixelData GetBitmapData(HBITMAP handle, const BITMAPINFO& info, HDC hdc) { if (result == 0) data.reset(); - return data.Pass(); + return data; } // Checks if the specifed row is transparent in provided bitmap. diff --git a/chromium/ui/views/corewm/tooltip.h b/chromium/ui/views/corewm/tooltip.h index b8e6803a172..387fa3c51d3 100644 --- a/chromium/ui/views/corewm/tooltip.h +++ b/chromium/ui/views/corewm/tooltip.h @@ -26,8 +26,7 @@ class VIEWS_EXPORT Tooltip { virtual ~Tooltip() {} // Returns the max width of the tooltip when shown at the specified location. - virtual int GetMaxWidth(const gfx::Point& location, - aura::Window* context) const = 0; + virtual int GetMaxWidth(const gfx::Point& location) const = 0; // Updates the text on the tooltip and resizes to fit. virtual void SetText(aura::Window* window, diff --git a/chromium/ui/views/corewm/tooltip_aura.cc b/chromium/ui/views/corewm/tooltip_aura.cc index 060d43453fb..80dac9fd8f6 100644 --- a/chromium/ui/views/corewm/tooltip_aura.cc +++ b/chromium/ui/views/corewm/tooltip_aura.cc @@ -138,7 +138,7 @@ void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, const gfx::Size& tooltip_size) { gfx::Rect tooltip_rect(mouse_pos, tooltip_size); tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); - gfx::Screen* screen = gfx::Screen::GetScreenFor(tooltip_window_); + gfx::Screen* screen = gfx::Screen::GetScreen(); gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); // If tooltip is out of bounds on the x axis, we simply shift it @@ -165,9 +165,8 @@ void TooltipAura::DestroyWidget() { } } -int TooltipAura::GetMaxWidth(const gfx::Point& location, - aura::Window* context) const { - gfx::Screen* screen = gfx::Screen::GetScreenFor(context); +int TooltipAura::GetMaxWidth(const gfx::Point& location) const { + gfx::Screen* screen = gfx::Screen::GetScreen(); gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); return std::min(kTooltipMaxWidthPixels, (display_bounds.width() + 1) / 2); } @@ -176,7 +175,7 @@ void TooltipAura::SetText(aura::Window* window, const base::string16& tooltip_text, const gfx::Point& location) { tooltip_window_ = window; - tooltip_view_->SetMaxWidth(GetMaxWidth(location, window)); + tooltip_view_->SetMaxWidth(GetMaxWidth(location)); tooltip_view_->SetText(tooltip_text); if (!widget_) { diff --git a/chromium/ui/views/corewm/tooltip_aura.h b/chromium/ui/views/corewm/tooltip_aura.h index a905fbbe1ac..781acf97c41 100644 --- a/chromium/ui/views/corewm/tooltip_aura.h +++ b/chromium/ui/views/corewm/tooltip_aura.h @@ -7,7 +7,6 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "ui/gfx/screen_type_delegate.h" #include "ui/views/corewm/tooltip.h" #include "ui/views/widget/widget_observer.h" @@ -40,8 +39,7 @@ class VIEWS_EXPORT TooltipAura : public Tooltip, public WidgetObserver { void DestroyWidget(); // Tooltip: - int GetMaxWidth(const gfx::Point& location, - aura::Window* context) const override; + int GetMaxWidth(const gfx::Point& location) const override; void SetText(aura::Window* window, const base::string16& tooltip_text, const gfx::Point& location) override; diff --git a/chromium/ui/views/corewm/tooltip_controller.cc b/chromium/ui/views/corewm/tooltip_controller.cc index ca27ffda393..c5dad8c4598 100644 --- a/chromium/ui/views/corewm/tooltip_controller.cc +++ b/chromium/ui/views/corewm/tooltip_controller.cc @@ -97,7 +97,7 @@ aura::Window* GetTooltipTarget(const ui::MouseEvent& event, gfx::Point screen_loc(event.location()); aura::client::GetScreenPositionClient(event_target->GetRootWindow())-> ConvertPointToScreen(event_target, &screen_loc); - gfx::Screen* screen = gfx::Screen::GetScreenFor(event_target); + gfx::Screen* screen = gfx::Screen::GetScreen(); aura::Window* target = screen->GetWindowAtScreenPoint(screen_loc); if (!target) return NULL; @@ -140,9 +140,8 @@ TooltipController::~TooltipController() { tooltip_window_->RemoveObserver(this); } -int TooltipController::GetMaxWidth(const gfx::Point& location, - gfx::NativeView context) const { - return tooltip_->GetMaxWidth(location, context); +int TooltipController::GetMaxWidth(const gfx::Point& location) const { + return tooltip_->GetMaxWidth(location); } void TooltipController::UpdateTooltip(aura::Window* target) { @@ -233,6 +232,10 @@ void TooltipController::OnMouseEvent(ui::MouseEvent* event) { // Hide the tooltip for click, release, drag, wheel events. if (tooltip_->IsVisible()) tooltip_->Hide(); + + // Don't reshow the tooltip during scroll. + if (tooltip_timer_.IsRunning()) + tooltip_timer_.Reset(); break; default: break; diff --git a/chromium/ui/views/corewm/tooltip_controller.h b/chromium/ui/views/corewm/tooltip_controller.h index ee42e723c92..5171726c5dc 100644 --- a/chromium/ui/views/corewm/tooltip_controller.h +++ b/chromium/ui/views/corewm/tooltip_controller.h @@ -39,8 +39,7 @@ class VIEWS_EXPORT TooltipController : public aura::client::TooltipClient, ~TooltipController() override; // Overridden from aura::client::TooltipClient. - int GetMaxWidth(const gfx::Point& location, - aura::Window* context) const override; + int GetMaxWidth(const gfx::Point& location) const override; void UpdateTooltip(aura::Window* target) override; void SetTooltipShownTimeout(aura::Window* target, int timeout_in_ms) override; void SetTooltipsEnabled(bool enable) override; diff --git a/chromium/ui/views/corewm/tooltip_controller_unittest.cc b/chromium/ui/views/corewm/tooltip_controller_unittest.cc index ca663228077..4fc0bc8e428 100644 --- a/chromium/ui/views/corewm/tooltip_controller_unittest.cc +++ b/chromium/ui/views/corewm/tooltip_controller_unittest.cc @@ -19,7 +19,6 @@ #include "ui/gfx/font.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/screen.h" -#include "ui/gfx/screen_type_delegate.h" #include "ui/gfx/text_elider.h" #include "ui/views/corewm/tooltip_aura.h" #include "ui/views/corewm/tooltip_controller_test_helper.h" @@ -427,14 +426,13 @@ class TooltipControllerCaptureTest : public TooltipControllerTest { &screen_position_client_); #if !defined(OS_CHROMEOS) desktop_screen_.reset(CreateDesktopScreen()); - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, - desktop_screen_.get()); + gfx::Screen::SetScreenInstance(desktop_screen_.get()); #endif } void TearDown() override { #if !defined(OS_CHROMEOS) - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen()); + gfx::Screen::SetScreenInstance(test_screen()); desktop_screen_.reset(); #endif aura::client::SetScreenPositionClient(GetRootWindow(), NULL); @@ -534,8 +532,7 @@ class TestTooltip : public Tooltip { const base::string16& tooltip_text() const { return tooltip_text_; } // Tooltip: - int GetMaxWidth(const gfx::Point& location, - aura::Window* context) const override { + int GetMaxWidth(const gfx::Point& location) const override { return 100; } void SetText(aura::Window* window, diff --git a/chromium/ui/views/corewm/tooltip_win.cc b/chromium/ui/views/corewm/tooltip_win.cc index 46635d2ba5d..b892d3871a2 100644 --- a/chromium/ui/views/corewm/tooltip_win.cc +++ b/chromium/ui/views/corewm/tooltip_win.cc @@ -77,7 +77,6 @@ bool TooltipWin::EnsureTooltipWindow() { } void TooltipWin::PositionTooltip() { - // This code only runs for non-metro, so GetNativeScreen() is fine. gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_); const int cursoroffset = GetCurrentCursorVisibleHeight(); screen_point.Offset(0, cursoroffset); @@ -87,7 +86,7 @@ void TooltipWin::PositionTooltip() { const gfx::Size size(LOWORD(tooltip_size), HIWORD(tooltip_size)); const gfx::Display display( - gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point)); + gfx::Screen::GetScreen()->GetDisplayNearestPoint(screen_point)); gfx::Rect tooltip_bounds(screen_point, size); tooltip_bounds.AdjustToFit(gfx::win::DIPToScreenRect(display.work_area())); @@ -95,12 +94,10 @@ void TooltipWin::PositionTooltip() { 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } -int TooltipWin::GetMaxWidth(const gfx::Point& location, - aura::Window* context) const { - // This code only runs for non-metro, so GetNativeScreen() is fine. +int TooltipWin::GetMaxWidth(const gfx::Point& location) const { const gfx::Point screen_point = gfx::win::DIPToScreenPoint(location); gfx::Display display( - gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point)); + gfx::Screen::GetScreen()->GetDisplayNearestPoint(screen_point)); const gfx::Rect monitor_bounds = display.bounds(); return (monitor_bounds.width() + 1) / 2; } @@ -126,7 +123,7 @@ void TooltipWin::SetText(aura::Window* window, SendMessage(tooltip_hwnd_, TTM_SETTOOLINFO, 0, reinterpret_cast<LPARAM>(&toolinfo_)); - int max_width = GetMaxWidth(location_, window); + int max_width = GetMaxWidth(location_); SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, max_width); } diff --git a/chromium/ui/views/corewm/tooltip_win.h b/chromium/ui/views/corewm/tooltip_win.h index 0b8ea61ecc3..eb9e5209de3 100644 --- a/chromium/ui/views/corewm/tooltip_win.h +++ b/chromium/ui/views/corewm/tooltip_win.h @@ -37,8 +37,7 @@ class VIEWS_EXPORT TooltipWin : public Tooltip { void PositionTooltip(); // Tooltip: - int GetMaxWidth(const gfx::Point& location, - aura::Window* context) const override; + int GetMaxWidth(const gfx::Point& location) const override; void SetText(aura::Window* window, const base::string16& tooltip_text, const gfx::Point& location) override; diff --git a/chromium/ui/views/drag_utils.cc b/chromium/ui/views/drag_utils.cc index 2597bf2936b..9eae9f6472b 100644 --- a/chromium/ui/views/drag_utils.cc +++ b/chromium/ui/views/drag_utils.cc @@ -14,13 +14,10 @@ namespace { float GetDeviceScaleForNativeView(views::Widget* widget) { float device_scale = 1.0f; - // The following code should work on other platforms as well. But we do not - // yet care about device scale factor on other platforms. So to keep drag and - // drop behavior on other platforms un-touched, we wrap this in the #if guard. if (widget && widget->GetNativeView()) { gfx::NativeView view = widget->GetNativeView(); - gfx::Display display = gfx::Screen::GetScreenFor(view)-> - GetDisplayNearestWindow(view); + gfx::Display display = + gfx::Screen::GetScreen()->GetDisplayNearestWindow(view); device_scale = display.device_scale_factor(); } return device_scale; diff --git a/chromium/ui/views/event_monitor_mac.mm b/chromium/ui/views/event_monitor_mac.mm index 45578c9948a..f21aae7cdd3 100644 --- a/chromium/ui/views/event_monitor_mac.mm +++ b/chromium/ui/views/event_monitor_mac.mm @@ -10,6 +10,7 @@ #include "ui/events/event.h" #include "ui/events/event_handler.h" #include "ui/events/event_utils.h" +#include "ui/gfx/screen.h" namespace views { @@ -28,11 +29,7 @@ scoped_ptr<EventMonitor> EventMonitor::CreateWindowMonitor( // static gfx::Point EventMonitor::GetLastMouseLocation() { - NSPoint mouseLocation = [NSEvent mouseLocation]; - // Flip coordinates to gfx (0,0 in top-left corner) using primary screen. - NSScreen* screen = [[NSScreen screens] firstObject]; - mouseLocation.y = NSMaxY([screen frame]) - mouseLocation.y; - return gfx::Point(mouseLocation.x, mouseLocation.y); + return gfx::Screen::GetScreen()->GetCursorScreenPoint(); } EventMonitorMac::EventMonitorMac(ui::EventHandler* event_handler, diff --git a/chromium/ui/views/examples/button_example.cc b/chromium/ui/views/examples/button_example.cc index 5b0b1e51365..2cf42afa9e3 100644 --- a/chromium/ui/views/examples/button_example.cc +++ b/chromium/ui/views/examples/button_example.cc @@ -65,8 +65,16 @@ void ButtonExample::CreateExampleView(View* container) { container->AddChildView(new BlueButton(this, ASCIIToUTF16("Blue Button"))); - container->AddChildView( - new MdTextButton(nullptr, base::ASCIIToUTF16("Material design"))); + container->AddChildView(MdTextButton::CreateMdButton( + nullptr, base::ASCIIToUTF16("Material design"))); + MdTextButton* md_button = MdTextButton::CreateMdButton( + nullptr, base::ASCIIToUTF16("Strong call to action")); + md_button->SetCallToAction(MdTextButton::STRONG_CALL_TO_ACTION); + container->AddChildView(md_button); + md_button = MdTextButton::CreateMdButton( + nullptr, base::ASCIIToUTF16("Weak call to action")); + md_button->SetCallToAction(MdTextButton::WEAK_CALL_TO_ACTION); + container->AddChildView(md_button); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); image_button_ = new ImageButton(this); diff --git a/chromium/ui/views/examples/combobox_example.cc b/chromium/ui/views/examples/combobox_example.cc index ed07379b605..4c4fdb300b9 100644 --- a/chromium/ui/views/examples/combobox_example.cc +++ b/chromium/ui/views/examples/combobox_example.cc @@ -7,7 +7,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "ui/views/controls/combobox/combobox.h" -#include "ui/views/layout/fill_layout.h" +#include "ui/views/layout/box_layout.h" namespace views { namespace examples { @@ -26,13 +26,17 @@ base::string16 ComboboxModelExample::GetItemAt(int index) { return base::UTF8ToUTF16(base::StringPrintf("Item %d", index)); } -ComboboxExample::ComboboxExample() : ExampleBase("Combo Box"), combobox_(NULL) { +ComboboxExample::ComboboxExample() : ExampleBase("Combo Box") { } ComboboxExample::~ComboboxExample() { // Delete |combobox_| first as it references |combobox_model_|. delete combobox_; - combobox_ = NULL; + delete disabled_combobox_; + delete action_combobox_; + combobox_ = nullptr; + disabled_combobox_ = nullptr; + action_combobox_ = nullptr; } void ComboboxExample::CreateExampleView(View* container) { @@ -40,14 +44,35 @@ void ComboboxExample::CreateExampleView(View* container) { combobox_->set_listener(this); combobox_->SetSelectedIndex(3); - container->SetLayoutManager(new FillLayout); + disabled_combobox_ = new Combobox(&combobox_model_); + disabled_combobox_->set_listener(this); + disabled_combobox_->SetSelectedIndex(4); + disabled_combobox_->SetEnabled(false); + + action_combobox_ = new Combobox(&combobox_model_); + action_combobox_->set_listener(this); + action_combobox_->SetStyle(Combobox::STYLE_ACTION); + // Note: STYLE_ACTION comboboxes always have the first item selected by + // default. + + container->SetLayoutManager(new BoxLayout( + BoxLayout::kVertical, + 1, 1, 1)); container->AddChildView(combobox_); + container->AddChildView(disabled_combobox_); + container->AddChildView(action_combobox_); } void ComboboxExample::OnPerformAction(Combobox* combobox) { - DCHECK_EQ(combobox_, combobox); - PrintStatus("Selected: %s", base::UTF16ToUTF8(combobox_model_.GetItemAt( - combobox->selected_index())).c_str()); + if (combobox == combobox_) { + PrintStatus("Selected: %s", base::UTF16ToUTF8(combobox_model_.GetItemAt( + combobox->selected_index())).c_str()); + } else if (combobox == action_combobox_) { + PrintStatus("Action: %s", base::UTF16ToUTF8(combobox_model_.GetItemAt( + combobox->selected_index())).c_str()); + } else { + NOTREACHED() << "Surprising combobox."; + } } } // namespace examples diff --git a/chromium/ui/views/examples/combobox_example.h b/chromium/ui/views/examples/combobox_example.h index 9c84b75539f..a2448b2fc50 100644 --- a/chromium/ui/views/examples/combobox_example.h +++ b/chromium/ui/views/examples/combobox_example.h @@ -41,7 +41,9 @@ class VIEWS_EXAMPLES_EXPORT ComboboxExample : public ExampleBase, void OnPerformAction(Combobox* combobox) override; ComboboxModelExample combobox_model_; - Combobox* combobox_; + Combobox* combobox_ = nullptr; + Combobox* disabled_combobox_ = nullptr; + Combobox* action_combobox_ = nullptr; DISALLOW_COPY_AND_ASSIGN(ComboboxExample); }; diff --git a/chromium/ui/views/examples/examples.gyp b/chromium/ui/views/examples/examples.gyp index 1c1c3ddbfce..50790ad639a 100644 --- a/chromium/ui/views/examples/examples.gyp +++ b/chromium/ui/views/examples/examples.gyp @@ -161,8 +161,6 @@ 'target_name': 'views_examples_with_content_exe', 'type': 'executable', 'dependencies': [ - '../../../base/base.gyp:base', - '../../../content/content.gyp:content', '../../views_content_client/views_content_client.gyp:views_content_client', 'views_examples_with_content_lib', ], @@ -171,6 +169,12 @@ 'examples_with_content_main_exe.cc', ], 'conditions': [ + ['component=="shared_library"', { + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../content/content.gyp:content', + ], + }], ['OS=="win"', { 'link_settings': { 'libraries': [ @@ -193,11 +197,6 @@ '../../../content/content.gyp:sandbox_helper_win', ], }], - ['OS=="win" and component!="shared_library" and win_use_allocator_shim==1', { - 'dependencies': [ - '<(DEPTH)/base/allocator/allocator.gyp:allocator', - ], - }], ], }, # target_name: views_examples_with_content_exe ], diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index 67ed52f5d6a..11096a781c1 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -37,7 +37,7 @@ #endif #if defined(USE_X11) -#include "ui/gfx/x/x11_connection.h" +#include "ui/gfx/x/x11_connection.h" // nogncheck #endif int main(int argc, char** argv) { @@ -93,8 +93,7 @@ int main(int argc, char** argv) { #endif #if !defined(OS_CHROMEOS) && defined(USE_AURA) scoped_ptr<gfx::Screen> desktop_screen(views::CreateDesktopScreen()); - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, - desktop_screen.get()); + gfx::Screen::SetScreenInstance(desktop_screen.get()); #endif views::examples::ShowExamplesWindow( diff --git a/chromium/ui/views/examples/menu_example.cc b/chromium/ui/views/examples/menu_example.cc index effa3e71713..166a792a847 100644 --- a/chromium/ui/views/examples/menu_example.cc +++ b/chromium/ui/views/examples/menu_example.cc @@ -65,7 +65,9 @@ class ExampleMenuButton : public MenuButton, public MenuButtonListener { private: // MenuButtonListener: - void OnMenuButtonClicked(View* source, const gfx::Point& point) override; + void OnMenuButtonClicked(MenuButton* source, + const gfx::Point& point, + const ui::Event* event) override; ui::SimpleMenuModel* GetMenuModel(); @@ -178,14 +180,14 @@ void ExampleMenuModel::ExecuteCommand(int command_id, int event_flags) { // ExampleMenuButton ----------------------------------------------------------- ExampleMenuButton::ExampleMenuButton(const base::string16& test) - : MenuButton(NULL, test, this, true) { -} + : MenuButton(test, this, true) {} ExampleMenuButton::~ExampleMenuButton() { } -void ExampleMenuButton::OnMenuButtonClicked(View* source, - const gfx::Point& point) { +void ExampleMenuButton::OnMenuButtonClicked(MenuButton* source, + const gfx::Point& point, + const ui::Event* event) { menu_runner_.reset(new MenuRunner(GetMenuModel(), MenuRunner::HAS_MNEMONICS)); if (menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), diff --git a/chromium/ui/views/examples/multiline_example.cc b/chromium/ui/views/examples/multiline_example.cc index 5ce1a1bddfb..d01b31e2d91 100644 --- a/chromium/ui/views/examples/multiline_example.cc +++ b/chromium/ui/views/examples/multiline_example.cc @@ -25,7 +25,7 @@ namespace examples { namespace { -gfx::Range ClampRange(gfx::Range range, size_t max) { +gfx::Range ClampRange(gfx::Range range, uint32_t max) { range.set_start(std::min(range.start(), max)); range.set_end(std::min(range.end(), max)); return range; diff --git a/chromium/ui/views/examples/widget_example.cc b/chromium/ui/views/examples/widget_example.cc index bcfb451aa85..0165de3f9dc 100644 --- a/chromium/ui/views/examples/widget_example.cc +++ b/chromium/ui/views/examples/widget_example.cc @@ -28,7 +28,6 @@ class DialogExample : public DialogDelegateView { ~DialogExample() override; base::string16 GetWindowTitle() const override; View* CreateExtraView() override; - View* CreateTitlebarExtraView() override; View* CreateFootnoteView() override; }; @@ -61,12 +60,6 @@ View* DialogExample::CreateExtraView() { return button; } -View* DialogExample::CreateTitlebarExtraView() { - Label* label = new Label(ASCIIToUTF16("Extra view!")); - label->SetEnabledColor(SK_ColorBLUE); - return label; -} - View* DialogExample::CreateFootnoteView() { return new Label(ASCIIToUTF16("Footnote label!")); } diff --git a/chromium/ui/views/layout/box_layout.cc b/chromium/ui/views/layout/box_layout.cc index 10f82b9aa54..9c3922e7e86 100644 --- a/chromium/ui/views/layout/box_layout.cc +++ b/chromium/ui/views/layout/box_layout.cc @@ -15,8 +15,6 @@ BoxLayout::BoxLayout(BoxLayout::Orientation orientation, int between_child_spacing) : orientation_(orientation), inside_border_insets_(inside_border_vertical_spacing, - inside_border_horizontal_spacing, - inside_border_vertical_spacing, inside_border_horizontal_spacing), between_child_spacing_(between_child_spacing), main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), diff --git a/chromium/ui/views/layout/layout_constants.h b/chromium/ui/views/layout/layout_constants.h index 28d16b71c74..de062dd8aad 100644 --- a/chromium/ui/views/layout/layout_constants.h +++ b/chromium/ui/views/layout/layout_constants.h @@ -20,9 +20,6 @@ const int kPanelHorizMargin = 13; // Top or bottom margin. const int kPanelVertMargin = 13; -// If some UI has some sub UI. Indent horizontally by the following value. -const int kPanelHorizIndentation = 24; - // When several controls are aligned vertically, the baseline should be spaced // by the following number of pixels. const int kPanelVerticalSpacing = 32; diff --git a/chromium/ui/views/linux_ui/linux_ui.cc b/chromium/ui/views/linux_ui/linux_ui.cc index 63c74020c19..9430addaabe 100644 --- a/chromium/ui/views/linux_ui/linux_ui.cc +++ b/chromium/ui/views/linux_ui/linux_ui.cc @@ -6,7 +6,7 @@ #include "ui/base/ime/linux/linux_input_method_context_factory.h" #include "ui/gfx/linux_font_delegate.h" -#include "ui/shell_dialogs/linux_shell_dialog.h" +#include "ui/shell_dialogs/shell_dialog_linux.h" namespace { @@ -21,7 +21,7 @@ void LinuxUI::SetInstance(LinuxUI* instance) { g_linux_ui = instance; LinuxInputMethodContextFactory::SetInstance(instance); LinuxFontDelegate::SetInstance(instance); - LinuxShellDialog::SetInstance(instance); + ShellDialogLinux::SetInstance(instance); ui::SetTextEditKeyBindingsDelegate(instance); } diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h index 0a28b2e7c49..d795d7c488a 100644 --- a/chromium/ui/views/linux_ui/linux_ui.h +++ b/chromium/ui/views/linux_ui/linux_ui.h @@ -12,7 +12,7 @@ #include "ui/base/ime/linux/linux_input_method_context_factory.h" #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h" #include "ui/gfx/linux_font_delegate.h" -#include "ui/shell_dialogs/linux_shell_dialog.h" +#include "ui/shell_dialogs/shell_dialog_linux.h" #include "ui/views/controls/button/button.h" #include "ui/views/linux_ui/status_icon_linux.h" #include "ui/views/views_export.h" @@ -52,7 +52,7 @@ class WindowButtonOrderObserver; // liuigtk3.so, etc. class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, public gfx::LinuxFontDelegate, - public ui::LinuxShellDialog, + public ui::ShellDialogLinux, public ui::TextEditKeyBindingsDelegateAuraLinux { public: // Describes the window management actions that could be taken in response to diff --git a/chromium/ui/views/mouse_watcher_view_host.cc b/chromium/ui/views/mouse_watcher_view_host.cc index 54412cf10d5..ac02b3cf4bf 100644 --- a/chromium/ui/views/mouse_watcher_view_host.cc +++ b/chromium/ui/views/mouse_watcher_view_host.cc @@ -49,8 +49,8 @@ bool MouseWatcherViewHost::IsMouseOverWindow() { if (!widget) return false; - return gfx::Screen::GetScreenFor(widget->GetNativeView())-> - GetWindowUnderCursor() == widget->GetNativeWindow(); + return gfx::Screen::GetScreen()->GetWindowUnderCursor() == + widget->GetNativeWindow(); } } // namespace views diff --git a/chromium/ui/views/mus/BUILD.gn b/chromium/ui/views/mus/BUILD.gn index f3863894939..a9005760549 100644 --- a/chromium/ui/views/mus/BUILD.gn +++ b/chromium/ui/views/mus/BUILD.gn @@ -23,6 +23,9 @@ component("mus") { "native_widget_mus.h", "platform_window_mus.cc", "platform_window_mus.h", + "screen_mus.cc", + "screen_mus.h", + "screen_mus_delegate.h", "surface_binding.cc", "surface_binding.h", "surface_context_factory.cc", @@ -59,7 +62,6 @@ component("mus") { "//mojo/converters/geometry", "//mojo/converters/ime", "//mojo/converters/input_events", - "//mojo/converters/network", "//mojo/converters/surfaces", "//mojo/platform_handle:for_component", "//mojo/public/c/system:for_component", @@ -129,31 +131,41 @@ group("tests") { testonly = true deps = [ - ":views_apptests", + ":views_mus_unittests", ] } -mojo_native_application("views_apptests") { +test("views_mus_unittests") { testonly = true - configs = [ "//build/config:precompiled_headers" ] + configs += [ "//build/config:precompiled_headers" ] # TODO(sky): add more files. sources = [ + "../run_all_unittests.cc", + "../run_all_unittests.h", + "../test/native_widget_factory_mus.cc", + "../view_targeter_unittest.cc", "../widget/native_widget_unittest.cc", - "../widget/root_view_unittest.cc", "../widget/widget_unittest.cc", + "native_widget_mus_unittest.cc", "platform_test_helper_mus.cc", + "run_all_unittests_mus.cc", ] deps = [ ":mus", "//base", "//base:i18n", - "//base/allocator", "//base/test:test_support", "//cc", - "//mojo/shell/public/cpp:test_support", + "//components/mus/public/cpp", + "//components/mus/public/interfaces", + "//mojo/shell/background:lib", + "//mojo/shell/background:main", + "//mojo/shell/background/tests:test_support", + "//mojo/shell/public/cpp:sources", + "//mojo/shell/runner/host:lib", "//skia", "//testing/gtest", "//third_party/icu", @@ -182,7 +194,12 @@ mojo_native_application("views_apptests") { data_deps = [ "//mash/wm", - "//ui/resources:ui_test_pak", + ] + + # TODO(thakis): This should be a data_deps on //ui/resources:ui_test_pak, but + # that has no effect. (See similar TODOs elsewhere ui_test.pak is listed) + data = [ + "$root_out_dir/ui_test.pak", ] if (is_win) { diff --git a/chromium/ui/views/mus/DEPS b/chromium/ui/views/mus/DEPS index 22b8edb4b19..a6ef8809458 100644 --- a/chromium/ui/views/mus/DEPS +++ b/chromium/ui/views/mus/DEPS @@ -22,3 +22,9 @@ include_rules = [ "+ui/platform_window", "+ui/wm", ] + +specific_include_rules = { + "platform_test_helper_mus.cc": [ + "+mojo/shell/background", + ], +} diff --git a/chromium/ui/views/mus/aura_init.cc b/chromium/ui/views/mus/aura_init.cc index f7f30b0f24a..2b08108882e 100644 --- a/chromium/ui/views/mus/aura_init.cc +++ b/chromium/ui/views/mus/aura_init.cc @@ -11,7 +11,7 @@ #include "base/path_service.h" #include "build/build_config.h" #include "components/resource_provider/public/cpp/resource_loader.h" -#include "mojo/shell/public/cpp/application_impl.h" +#include "mojo/shell/public/cpp/connector.h" #include "ui/aura/env.h" #include "ui/base/ime/input_method_initializer.h" #include "ui/base/resource/resource_bundle.h" @@ -50,12 +50,12 @@ class MusViewsDelegate : public ViewsDelegate { } // namespace -AuraInit::AuraInit(mojo::ApplicationImpl* app, const std::string& resource_file) +AuraInit::AuraInit(mojo::Connector* connector, const std::string& resource_file) : resource_file_(resource_file), views_delegate_(new MusViewsDelegate) { aura::Env::CreateInstance(false); - InitializeResources(app); + InitializeResources(connector); ui::InitializeInputMethodForTesting(); } @@ -72,13 +72,12 @@ AuraInit::~AuraInit() { #endif } -void AuraInit::InitializeResources(mojo::ApplicationImpl* app) { +void AuraInit::InitializeResources(mojo::Connector* connector) { if (ui::ResourceBundle::HasSharedInstance()) return; resource_provider::ResourceLoader resource_loader( - app, GetResourcePaths(resource_file_)); - if (!resource_loader.BlockUntilLoaded()) - return; + connector, GetResourcePaths(resource_file_)); + CHECK(resource_loader.BlockUntilLoaded()); CHECK(resource_loader.loaded()); ui::RegisterPathProvider(); base::File pak_file = resource_loader.ReleaseFile(resource_file_); @@ -90,7 +89,7 @@ void AuraInit::InitializeResources(mojo::ApplicationImpl* app) { // Initialize the skia font code to go ask fontconfig underneath. #if defined(OS_LINUX) && !defined(OS_ANDROID) - font_loader_ = skia::AdoptRef(new font_service::FontLoader(app->shell())); + font_loader_ = skia::AdoptRef(new font_service::FontLoader(connector)); SkFontConfigInterface::SetGlobal(font_loader_.get()); #endif diff --git a/chromium/ui/views/mus/aura_init.h b/chromium/ui/views/mus/aura_init.h index bff01716a2f..521acac91ee 100644 --- a/chromium/ui/views/mus/aura_init.h +++ b/chromium/ui/views/mus/aura_init.h @@ -18,7 +18,7 @@ class FontLoader; } namespace mojo { -class ApplicationImpl; +class Connector; } namespace views { @@ -28,11 +28,11 @@ class ViewsDelegate; // |resource_file| is the path to the apk file containing the resources. class VIEWS_MUS_EXPORT AuraInit { public: - AuraInit(mojo::ApplicationImpl* app, const std::string& resource_file); + AuraInit(mojo::Connector* connector, const std::string& resource_file); ~AuraInit(); private: - void InitializeResources(mojo::ApplicationImpl* app); + void InitializeResources(mojo::Connector* connector); #if defined(OS_LINUX) && !defined(OS_ANDROID) skia::RefPtr<font_service::FontLoader> font_loader_; diff --git a/chromium/ui/views/mus/input_method_mus.cc b/chromium/ui/views/mus/input_method_mus.cc index 3234f8fec7e..cee5ea671f7 100644 --- a/chromium/ui/views/mus/input_method_mus.cc +++ b/chromium/ui/views/mus/input_method_mus.cc @@ -103,4 +103,4 @@ void InputMethodMUS::UpdateTextInputType() { window_->SetTextInputState(std::move(state)); } -} // namespace mandoline +} // namespace views diff --git a/chromium/ui/views/mus/native_widget_mus.cc b/chromium/ui/views/mus/native_widget_mus.cc index b8038eb0032..70dd986d2c6 100644 --- a/chromium/ui/views/mus/native_widget_mus.cc +++ b/chromium/ui/views/mus/native_widget_mus.cc @@ -8,7 +8,9 @@ #include "base/thread_task_runner_handle.h" #include "components/mus/public/cpp/property_type_converters.h" #include "components/mus/public/cpp/window.h" +#include "components/mus/public/cpp/window_observer.h" #include "components/mus/public/cpp/window_property.h" +#include "components/mus/public/cpp/window_tree_connection.h" #include "mojo/converters/geometry/geometry_type_converters.h" #include "ui/aura/client/default_capture_client.h" #include "ui/aura/client/window_tree_client.h" @@ -25,10 +27,12 @@ #include "ui/views/mus/window_manager_constants_converters.h" #include "ui/views/mus/window_manager_frame_values.h" #include "ui/views/mus/window_tree_host_mus.h" +#include "ui/views/widget/native_widget_aura.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/window/custom_frame_view.h" #include "ui/wm/core/base_focus_rules.h" #include "ui/wm/core/capture_controller.h" +#include "ui/wm/core/default_screen_position_client.h" #include "ui/wm/core/focus_controller.h" DECLARE_WINDOW_PROPERTY_TYPE(mus::Window*); @@ -38,6 +42,8 @@ namespace { DEFINE_WINDOW_PROPERTY_KEY(mus::Window*, kMusWindow, nullptr); +MUS_DEFINE_WINDOW_PROPERTY_KEY(NativeWidgetMus*, kNativeWidgetMusKey, nullptr); + // TODO: figure out what this should be. class FocusRulesImpl : public wm::BaseFocusRules { public: @@ -149,43 +155,120 @@ class ClientSideNonClientFrameView : public NonClientFrameView { void SizeConstraintsChanged() override { // NOTIMPLEMENTED(); } + gfx::Size GetPreferredSize() const override { + return widget_->non_client_view() + ->GetWindowBoundsForClientBounds( + gfx::Rect(widget_->client_view()->GetPreferredSize())) + .size(); + } + gfx::Size GetMinimumSize() const override { + return widget_->non_client_view() + ->GetWindowBoundsForClientBounds( + gfx::Rect(widget_->client_view()->GetMinimumSize())) + .size(); + } + gfx::Size GetMaximumSize() const override { + gfx::Size max_size = widget_->client_view()->GetMaximumSize(); + gfx::Size converted_size = + widget_->non_client_view() + ->GetWindowBoundsForClientBounds(gfx::Rect(max_size)) + .size(); + return gfx::Size(max_size.width() == 0 ? 0 : converted_size.width(), + max_size.height() == 0 ? 0 : converted_size.height()); + } views::Widget* widget_; DISALLOW_COPY_AND_ASSIGN(ClientSideNonClientFrameView); }; -mus::mojom::ResizeBehavior ResizeBehaviorFromDelegate( - WidgetDelegate* delegate) { - int32_t behavior = mus::mojom::RESIZE_BEHAVIOR_NONE; +int ResizeBehaviorFromDelegate(WidgetDelegate* delegate) { + if (!delegate) + return mus::mojom::kResizeBehaviorNone; + + int32_t behavior = mus::mojom::kResizeBehaviorNone; if (delegate->CanResize()) - behavior |= mus::mojom::RESIZE_BEHAVIOR_CAN_RESIZE; + behavior |= mus::mojom::kResizeBehaviorCanResize; if (delegate->CanMaximize()) - behavior |= mus::mojom::RESIZE_BEHAVIOR_CAN_MAXIMIZE; + behavior |= mus::mojom::kResizeBehaviorCanMaximize; if (delegate->CanMinimize()) - behavior |= mus::mojom::RESIZE_BEHAVIOR_CAN_MINIMIZE; - return static_cast<mus::mojom::ResizeBehavior>(behavior); + behavior |= mus::mojom::kResizeBehaviorCanMinimize; + return behavior; +} + +// Returns the 1x window app icon or an empty SkBitmap if no icon is available. +// TODO(jamescook): Support other scale factors. +SkBitmap AppIconFromDelegate(WidgetDelegate* delegate) { + if (!delegate) + return SkBitmap(); + gfx::ImageSkia app_icon = delegate->GetWindowAppIcon(); + if (app_icon.isNull()) + return SkBitmap(); + return app_icon.GetRepresentation(1.f).sk_bitmap(); } } // namespace +class NativeWidgetMus::MusWindowObserver : public mus::WindowObserver { + public: + explicit MusWindowObserver(NativeWidgetMus* native_widget_mus) + : native_widget_mus_(native_widget_mus) { + native_widget_mus_->window_->AddObserver(this); + } + + ~MusWindowObserver() override { + native_widget_mus_->window_->RemoveObserver(this); + } + + // mus::WindowObserver: + void OnWindowVisibilityChanging(mus::Window* window) override { + native_widget_mus_->OnMusWindowVisibilityChanging(window); + } + void OnWindowVisibilityChanged(mus::Window* window) override { + native_widget_mus_->OnMusWindowVisibilityChanged(window); + } + + private: + NativeWidgetMus* native_widget_mus_; + + DISALLOW_COPY_AND_ASSIGN(MusWindowObserver); +}; + //////////////////////////////////////////////////////////////////////////////// // NativeWidgetMus, public: NativeWidgetMus::NativeWidgetMus(internal::NativeWidgetDelegate* delegate, - mojo::Shell* shell, + mojo::Connector* connector, mus::Window* window, mus::mojom::SurfaceType surface_type) : window_(window), - shell_(shell), native_widget_delegate_(delegate), surface_type_(surface_type), show_state_before_fullscreen_(ui::PLATFORM_WINDOW_STATE_UNKNOWN), ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), content_(new aura::Window(this)), close_widget_factory_(this) { + mus_window_observer_.reset(new MusWindowObserver(this)); + // TODO(fsamuel): Figure out lifetime of |window_|. aura::SetMusWindow(content_, window_); + + window->SetLocalProperty(kNativeWidgetMusKey, this); + // WindowTreeHost creates the compositor using the ContextFactory from + // aura::Env. Install |context_factory_| there so that |context_factory_| is + // picked up. + ui::ContextFactory* default_context_factory = + aura::Env::GetInstance()->context_factory(); + // For Chrome, we need the GpuProcessTransportFactory so that renderer and + // browser pixels are composited into a single backing + // SoftwareOutputDeviceMus. + if (!default_context_factory) { + context_factory_.reset( + new SurfaceContextFactory(connector, window_, surface_type_)); + aura::Env::GetInstance()->set_context_factory(context_factory_.get()); + } + window_tree_host_.reset(new WindowTreeHostMus(connector, this, window_)); + aura::Env::GetInstance()->set_context_factory(default_context_factory); } NativeWidgetMus::~NativeWidgetMus() { @@ -195,6 +278,24 @@ NativeWidgetMus::~NativeWidgetMus() { CloseNow(); } +// static +void NativeWidgetMus::NotifyFrameChanged( + mus::WindowTreeConnection* connection) { + for (mus::Window* window : connection->GetRoots()) { + NativeWidgetMus* native_widget = + window->GetLocalProperty(kNativeWidgetMusKey); + if (native_widget && native_widget->GetWidget()->non_client_view()) { + native_widget->GetWidget()->non_client_view()->Layout(); + native_widget->GetWidget()->non_client_view()->SchedulePaint(); + native_widget->UpdateClientArea(); + } + } +} + +aura::Window* NativeWidgetMus::GetRootWindow() { + return window_tree_host_->window(); +} + void NativeWidgetMus::OnPlatformWindowClosed() { native_widget_delegate_->OnNativeWidgetDestroying(); @@ -204,6 +305,8 @@ void NativeWidgetMus::OnPlatformWindowClosed() { window_tree_host_->RemoveObserver(this); window_tree_host_.reset(); + mus_window_observer_.reset(nullptr); + window_ = nullptr; content_ = nullptr; @@ -222,6 +325,7 @@ void NativeWidgetMus::OnActivationChanged(bool active) { native_widget_delegate_->OnNativeBlur(); GetWidget()->GetFocusManager()->StoreFocusedView(true); } + native_widget_delegate_->OnNativeWidgetActivationChanged(active); } void NativeWidgetMus::UpdateClientArea() { @@ -246,19 +350,23 @@ void NativeWidgetMus::ConfigurePropertiesForNewWindow( std::map<std::string, std::vector<uint8_t>>* properties) { if (!init_params.bounds.IsEmpty()) { (*properties)[mus::mojom::WindowManager::kUserSetBounds_Property] = - mojo::TypeConverter<const std::vector<uint8_t>, gfx::Rect>::Convert( - init_params.bounds); + mojo::ConvertTo<std::vector<uint8_t>>(init_params.bounds); } if (!Widget::RequiresNonClientView(init_params.type)) return; (*properties)[mus::mojom::WindowManager::kWindowType_Property] = - mojo::TypeConverter<const std::vector<uint8_t>, int32_t>::Convert( - mojo::ConvertTo<mus::mojom::WindowType>(init_params.type)); + mojo::ConvertTo<std::vector<uint8_t>>(static_cast<int32_t>( + mojo::ConvertTo<mus::mojom::WindowType>(init_params.type))); (*properties)[mus::mojom::WindowManager::kResizeBehavior_Property] = - mojo::TypeConverter<const std::vector<uint8_t>, int32_t>::Convert( + mojo::ConvertTo<std::vector<uint8_t>>( ResizeBehaviorFromDelegate(init_params.delegate)); + SkBitmap app_icon = AppIconFromDelegate(init_params.delegate); + if (!app_icon.isNull()) { + (*properties)[mus::mojom::WindowManager::kWindowAppIcon_Property] = + mojo::ConvertTo<std::vector<uint8_t>>(app_icon); + } } //////////////////////////////////////////////////////////////////////////////// @@ -269,30 +377,14 @@ NonClientFrameView* NativeWidgetMus::CreateNonClientFrameView() { } void NativeWidgetMus::InitNativeWidget(const Widget::InitParams& params) { + NativeWidgetAura::RegisterNativeWidgetForWindow(this, content_); + ownership_ = params.ownership; window_->SetCanFocus(params.activatable == Widget::InitParams::ACTIVATABLE_YES); - // WindowTreeHost creates the compositor using the ContextFactory from - // aura::Env. Install |context_factory_| there so that |context_factory_| is - // picked up. - ui::ContextFactory* default_context_factory = - aura::Env::GetInstance()->context_factory(); - // For Chrome, we need the GpuProcessTransportFactory so that renderer and - // browser pixels are composited into a single backing - // SoftwareOutputDeviceMus. - if (!default_context_factory) { - if (!context_factory_) { - context_factory_.reset(new SurfaceContextFactory(shell_, window_, - surface_type_)); - } - aura::Env::GetInstance()->set_context_factory(context_factory_.get()); - } - window_tree_host_.reset( - new WindowTreeHostMus(shell_, this, window_, surface_type_)); window_tree_host_->AddObserver(this); window_tree_host_->InitHost(); - aura::Env::GetInstance()->set_context_factory(default_context_factory); window_tree_host_->window()->SetProperty(kMusWindow, window_); focus_client_.reset(new wm::FocusController(new FocusRulesImpl)); @@ -301,6 +393,10 @@ void NativeWidgetMus::InitNativeWidget(const Widget::InitParams& params) { focus_client_.get()); aura::client::SetActivationClient(window_tree_host_->window(), focus_client_.get()); + screen_position_client_.reset(new wm::DefaultScreenPositionClient()); + aura::client::SetScreenPositionClient(window_tree_host_->window(), + screen_position_client_.get()); + window_tree_client_.reset( new NativeWidgetMusWindowTreeClient(window_tree_host_->window())); window_tree_host_->window()->AddPreTargetHandler(focus_client_.get()); @@ -311,7 +407,8 @@ void NativeWidgetMus::InitNativeWidget(const Widget::InitParams& params) { content_->SetType(ui::wm::WINDOW_TYPE_NORMAL); content_->Init(ui::LAYER_TEXTURED); - content_->Show(); + if (window_->visible()) + content_->Show(); content_->SetTransparent(true); content_->SetFillsBoundsCompletely(false); window_tree_host_->window()->AddChild(content_); @@ -324,6 +421,10 @@ void NativeWidgetMus::InitNativeWidget(const Widget::InitParams& params) { parent_mus->AddTransientWindow(window_); } + // TODO(sky): deal with show state. + if (!params.bounds.size().IsEmpty()) + SetBounds(params.bounds); + // TODO(beng): much else, see [Desktop]NativeWidgetAura. native_widget_delegate_->OnNativeWidgetCreated(false); @@ -356,7 +457,6 @@ Widget* NativeWidgetMus::GetWidget() { } const Widget* NativeWidgetMus::GetWidget() const { - // NOTIMPLEMENTED(); return native_widget_delegate_->AsWidget(); } @@ -373,11 +473,11 @@ Widget* NativeWidgetMus::GetTopLevelWidget() { } const ui::Compositor* NativeWidgetMus::GetCompositor() const { - return window_tree_host_->window()->layer()->GetCompositor(); + return window_tree_host_->compositor(); } const ui::Layer* NativeWidgetMus::GetLayer() const { - return window_tree_host_->window()->layer(); + return content_ ? content_->layer() : nullptr; } void NativeWidgetMus::ReorderNativeViews() { @@ -411,15 +511,17 @@ TooltipManager* NativeWidgetMus::GetTooltipManager() const { } void NativeWidgetMus::SetCapture() { - content_->SetCapture(); + if (content_) + content_->SetCapture(); } void NativeWidgetMus::ReleaseCapture() { - content_->ReleaseCapture(); + if (content_) + content_->ReleaseCapture(); } bool NativeWidgetMus::HasCapture() const { - return content_->HasCapture(); + return content_ && content_->HasCapture(); } ui::InputMethod* NativeWidgetMus::GetInputMethod() { @@ -439,18 +541,39 @@ void NativeWidgetMus::GetWindowPlacement( } bool NativeWidgetMus::SetWindowTitle(const base::string16& title) { - window_->SetSharedProperty<base::string16>( - mus::mojom::WindowManager::kWindowTitle_Property, title); + if (!window_) + return false; + const char* kWindowTitle_Property = + mus::mojom::WindowManager::kWindowTitle_Property; + const base::string16 current_title = + window_->HasSharedProperty(kWindowTitle_Property) + ? window_->GetSharedProperty<base::string16>(kWindowTitle_Property) + : base::string16(); + if (current_title == title) + return false; + window_->SetSharedProperty<base::string16>(kWindowTitle_Property, title); return true; } void NativeWidgetMus::SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { - // NOTIMPLEMENTED(); + const char* const kWindowAppIcon_Property = + mus::mojom::WindowManager::kWindowAppIcon_Property; + + if (!app_icon.isNull()) { + // Send the app icon 1x bitmap to the window manager. + // TODO(jamescook): Support other scale factors. + window_->SetSharedProperty<SkBitmap>( + kWindowAppIcon_Property, app_icon.GetRepresentation(1.f).sk_bitmap()); + } else if (window_->HasSharedProperty(kWindowAppIcon_Property)) { + // Remove the existing icon. + window_->ClearSharedProperty(kWindowAppIcon_Property); + } } void NativeWidgetMus::InitModalType(ui::ModalType modal_type) { - // NOTIMPLEMENTED(); + if (modal_type != ui::MODAL_TYPE_NONE) + window_->SetModal(); } gfx::Rect NativeWidgetMus::GetWindowBoundsInScreen() const { @@ -464,15 +587,36 @@ gfx::Rect NativeWidgetMus::GetClientAreaBoundsInScreen() const { } gfx::Rect NativeWidgetMus::GetRestoredBounds() const { - // NOTIMPLEMENTED(); - return gfx::Rect(); + // Restored bounds should only be relevant if the window is minimized, + // maximized, fullscreen or docked. However, in some places the code expects + // GetRestoredBounds() to return the current window bounds if the window is + // not in either state. + if (IsMinimized() || IsMaximized() || IsFullscreen()) { + const char* kRestoreBounds_Property = + mus::mojom::WindowManager::kRestoreBounds_Property; + if (window_->HasSharedProperty(kRestoreBounds_Property)) + return window_->GetSharedProperty<gfx::Rect>(kRestoreBounds_Property); + } + return GetWindowBoundsInScreen(); } void NativeWidgetMus::SetBounds(const gfx::Rect& bounds) { - window_tree_host_->SetBounds(bounds); + if (!window_tree_host_) + return; + + gfx::Size size(bounds.size()); + const gfx::Size min_size = GetMinimumSize(); + const gfx::Size max_size = GetMaximumSize(); + if (!max_size.IsEmpty()) + size.SetToMin(max_size); + size.SetToMax(min_size); + window_tree_host_->SetBounds(gfx::Rect(bounds.origin(), size)); } void NativeWidgetMus::SetSize(const gfx::Size& size) { + if (!window_tree_host_) + return; + gfx::Rect bounds = window_tree_host_->GetBounds(); SetBounds(gfx::Rect(bounds.origin(), size)); } @@ -503,7 +647,9 @@ void NativeWidgetMus::Close() { } void NativeWidgetMus::CloseNow() { - window_->Destroy(); + // Depending upon ownership |window_| may have been destroyed. + if (window_) + window_->Destroy(); } void NativeWidgetMus::Show() { @@ -511,6 +657,9 @@ void NativeWidgetMus::Show() { } void NativeWidgetMus::Hide() { + if (!window_tree_host_) + return; + window_tree_host_->Hide(); GetNativeWindow()->Hide(); } @@ -521,6 +670,9 @@ void NativeWidgetMus::ShowMaximizedWithBounds( } void NativeWidgetMus::ShowWithWindowState(ui::WindowShowState state) { + if (!window_tree_host_) + return; + window_tree_host_->Show(); GetNativeWindow()->Show(); if (state != ui::SHOW_STATE_INACTIVE) @@ -530,20 +682,23 @@ void NativeWidgetMus::ShowWithWindowState(ui::WindowShowState state) { bool NativeWidgetMus::IsVisible() const { // TODO(beng): this should probably be wired thru PlatformWindow. - return window_->visible(); + return window_ && window_->visible(); } void NativeWidgetMus::Activate() { - window_tree_host_->platform_window()->Activate(); + if (window_tree_host_) + window_tree_host_->platform_window()->Activate(); } void NativeWidgetMus::Deactivate() { - // NOTIMPLEMENTED(); + if (IsActive()) + window_->connection()->ClearFocus(); } bool NativeWidgetMus::IsActive() const { - // NOTIMPLEMENTED(); - return true; + mus::Window* focused = + window_ ? window_->connection()->GetFocusedWindow() : nullptr; + return focused && window_->Contains(focused); } void NativeWidgetMus::SetAlwaysOnTop(bool always_on_top) { @@ -560,27 +715,32 @@ void NativeWidgetMus::SetVisibleOnAllWorkspaces(bool always_visible) { } void NativeWidgetMus::Maximize() { - window_tree_host_->platform_window()->Maximize(); + if (window_tree_host_) + window_tree_host_->platform_window()->Maximize(); } void NativeWidgetMus::Minimize() { - window_tree_host_->platform_window()->Minimize(); + if (window_tree_host_) + window_tree_host_->platform_window()->Minimize(); } bool NativeWidgetMus::IsMaximized() const { - return window_tree_host_->show_state() == ui::PLATFORM_WINDOW_STATE_MAXIMIZED; + return window_tree_host_ && + window_tree_host_->show_state() == ui::PLATFORM_WINDOW_STATE_MAXIMIZED; } bool NativeWidgetMus::IsMinimized() const { - return window_tree_host_->show_state() == ui::PLATFORM_WINDOW_STATE_MINIMIZED; + return window_tree_host_ && + window_tree_host_->show_state() == ui::PLATFORM_WINDOW_STATE_MINIMIZED; } void NativeWidgetMus::Restore() { - window_tree_host_->platform_window()->Restore(); + if (window_tree_host_) + window_tree_host_->platform_window()->Restore(); } void NativeWidgetMus::SetFullscreen(bool fullscreen) { - if (IsFullscreen() == fullscreen) + if (!window_tree_host_ || IsFullscreen() == fullscreen) return; if (fullscreen) { show_state_before_fullscreen_ = window_tree_host_->show_state(); @@ -604,16 +764,14 @@ void NativeWidgetMus::SetFullscreen(bool fullscreen) { } bool NativeWidgetMus::IsFullscreen() const { - return window_tree_host_->show_state() == - ui::PLATFORM_WINDOW_STATE_FULLSCREEN; + return window_tree_host_ && + window_tree_host_->show_state() == + ui::PLATFORM_WINDOW_STATE_FULLSCREEN; } void NativeWidgetMus::SetOpacity(unsigned char opacity) { - // NOTIMPLEMENTED(); -} - -void NativeWidgetMus::SetUseDragFrame(bool use_drag_frame) { - // NOTIMPLEMENTED(); + if (window_) + window_->SetOpacity(opacity / 255.0); } void NativeWidgetMus::FlashFrame(bool flash_frame) { @@ -635,6 +793,8 @@ void NativeWidgetMus::SchedulePaintInRect(const gfx::Rect& rect) { } void NativeWidgetMus::SetCursor(gfx::NativeCursor cursor) { + if (!window_tree_host_) + return; // TODO(erg): In aura, our incoming cursor is really two // parts. cursor.native_type() is an integer for standard cursors and is all // we support right now. If native_type() == kCursorCustom, than we should @@ -651,7 +811,12 @@ bool NativeWidgetMus::IsMouseEventsEnabled() const { } void NativeWidgetMus::ClearNativeFocus() { - // NOTIMPLEMENTED(); + if (!IsActive()) + return; + mus::Window* focused = + window_ ? window_->connection()->GetFocusedWindow() : nullptr; + if (focused && window_->Contains(focused) && focused != window_) + window_->SetFocus(); } gfx::Rect NativeWidgetMus::GetWorkAreaBoundsInScreen() const { @@ -699,6 +864,9 @@ bool NativeWidgetMus::IsTranslucentWindowOpacitySupported() const { } void NativeWidgetMus::OnSizeConstraintsChanged() { + if (!window_) + return; + window_->SetSharedProperty<int32_t>( mus::mojom::WindowManager::kResizeBehavior_Property, ResizeBehaviorFromDelegate(GetWidget()->widget_delegate())); @@ -813,7 +981,7 @@ void NativeWidgetMus::OnScrollEvent(ui::ScrollEvent* event) { return; // Convert unprocessed scroll events into wheel events. - ui::MouseWheelEvent mwe(*static_cast<ui::ScrollEvent*>(event)); + ui::MouseWheelEvent mwe(*event->AsScrollEvent()); native_widget_delegate_->OnMouseEvent(&mwe); if (mwe.handled()) event->SetHandled(); @@ -830,4 +998,19 @@ void NativeWidgetMus::OnHostCloseRequested(const aura::WindowTreeHost* host) { GetWidget()->Close(); } +void NativeWidgetMus::OnMusWindowVisibilityChanging(mus::Window* window) { + native_widget_delegate_->OnNativeWidgetVisibilityChanging(!window->visible()); +} + +void NativeWidgetMus::OnMusWindowVisibilityChanged(mus::Window* window) { + if (window->visible()) { + window_tree_host_->Show(); + GetNativeWindow()->Show(); + } else { + window_tree_host_->Hide(); + GetNativeWindow()->Hide(); + } + native_widget_delegate_->OnNativeWidgetVisibilityChanged(window->visible()); +} + } // namespace views diff --git a/chromium/ui/views/mus/native_widget_mus.h b/chromium/ui/views/mus/native_widget_mus.h index a940baf9158..3419c064e05 100644 --- a/chromium/ui/views/mus/native_widget_mus.h +++ b/chromium/ui/views/mus/native_widget_mus.h @@ -13,7 +13,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "components/mus/public/interfaces/window_manager.mojom.h" +#include "components/mus/public/interfaces/window_tree.mojom.h" #include "ui/aura/window_delegate.h" #include "ui/aura/window_tree_host_observer.h" #include "ui/platform_window/platform_window_delegate.h" @@ -23,21 +23,19 @@ namespace aura { namespace client { class DefaultCaptureClient; +class ScreenPositionClient; class WindowTreeClient; } class Window; } namespace mojo { -class Shell; +class Connector; } namespace mus { class Window; - -namespace mojom { -class WindowManager; -} +class WindowTreeConnection; } namespace wm { @@ -47,7 +45,6 @@ class FocusController; namespace views { class SurfaceContextFactory; class WidgetDelegate; -struct WindowManagerClientAreaInsets; class WindowTreeHostMus; // An implementation of NativeWidget that binds to a mus::Window. Because Aura @@ -61,7 +58,7 @@ class VIEWS_MUS_EXPORT NativeWidgetMus : public internal::NativeWidgetPrivate, public aura::WindowTreeHostObserver { public: NativeWidgetMus(internal::NativeWidgetDelegate* delegate, - mojo::Shell* shell, + mojo::Connector* connector, mus::Window* window, mus::mojom::SurfaceType surface_type); ~NativeWidgetMus() override; @@ -72,8 +69,13 @@ class VIEWS_MUS_EXPORT NativeWidgetMus : public internal::NativeWidgetPrivate, const Widget::InitParams& init_params, std::map<std::string, std::vector<uint8_t>>* properties); + // Notifies all widgets the frame constants changed in some way. + static void NotifyFrameChanged(mus::WindowTreeConnection* connection); + mus::Window* window() { return window_; } + aura::Window* GetRootWindow(); + void OnPlatformWindowClosed(); void OnActivationChanged(bool active); @@ -141,7 +143,6 @@ class VIEWS_MUS_EXPORT NativeWidgetMus : public internal::NativeWidgetPrivate, void SetFullscreen(bool fullscreen) override; bool IsFullscreen() const override; void SetOpacity(unsigned char opacity) override; - void SetUseDragFrame(bool use_drag_frame) override; void FlashFrame(bool flash_frame) override; void RunShellDrag(View* view, const ui::OSExchangeData& data, @@ -198,9 +199,12 @@ class VIEWS_MUS_EXPORT NativeWidgetMus : public internal::NativeWidgetPrivate, void OnHostCloseRequested(const aura::WindowTreeHost* host) override; private: - mus::Window* window_; + class MusWindowObserver; + + void OnMusWindowVisibilityChanging(mus::Window* window); + void OnMusWindowVisibilityChanged(mus::Window* window); - mojo::Shell* shell_; + mus::Window* window_; internal::NativeWidgetDelegate* native_widget_delegate_; @@ -210,6 +214,10 @@ class VIEWS_MUS_EXPORT NativeWidgetMus : public internal::NativeWidgetPrivate, // See class documentation for Widget in widget.h for a note about ownership. Widget::InitParams::Ownership ownership_; + // Functions with the same name require the mus::WindowObserver to be in + // a separate class. + scoped_ptr<MusWindowObserver> mus_window_observer_; + // Aura configuration. scoped_ptr<SurfaceContextFactory> context_factory_; scoped_ptr<WindowTreeHostMus> window_tree_host_; @@ -217,6 +225,7 @@ class VIEWS_MUS_EXPORT NativeWidgetMus : public internal::NativeWidgetPrivate, scoped_ptr<wm::FocusController> focus_client_; scoped_ptr<aura::client::DefaultCaptureClient> capture_client_; scoped_ptr<aura::client::WindowTreeClient> window_tree_client_; + scoped_ptr<aura::client::ScreenPositionClient> screen_position_client_; base::WeakPtrFactory<NativeWidgetMus> close_widget_factory_; DISALLOW_COPY_AND_ASSIGN(NativeWidgetMus); diff --git a/chromium/ui/views/mus/native_widget_mus_unittest.cc b/chromium/ui/views/mus/native_widget_mus_unittest.cc new file mode 100644 index 00000000000..78155814861 --- /dev/null +++ b/chromium/ui/views/mus/native_widget_mus_unittest.cc @@ -0,0 +1,197 @@ +// 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/views/mus/native_widget_mus.h" + +#include "base/macros.h" +#include "components/mus/public/cpp/property_type_converters.h" +#include "components/mus/public/cpp/window.h" +#include "components/mus/public/cpp/window_property.h" +#include "components/mus/public/interfaces/window_manager.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/aura/window.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/test/focus_manager_test.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/widget_observer.h" + +namespace views { +namespace { + +// Returns a small colored bitmap. +SkBitmap MakeBitmap(SkColor color) { + SkBitmap bitmap; + bitmap.allocN32Pixels(8, 8); + bitmap.eraseColor(color); + return bitmap; +} + +// An observer that tracks widget activation changes. +class WidgetActivationObserver : public WidgetObserver { + public: + explicit WidgetActivationObserver(Widget* widget) : widget_(widget) { + widget_->AddObserver(this); + } + + ~WidgetActivationObserver() override { + widget_->RemoveObserver(this); + } + + const std::vector<bool>& changes() const { return changes_; } + + // WidgetObserver: + void OnWidgetActivationChanged(Widget* widget, bool active) override { + ASSERT_EQ(widget_, widget); + changes_.push_back(active); + } + + private: + Widget* widget_; + std::vector<bool> changes_; + + DISALLOW_COPY_AND_ASSIGN(WidgetActivationObserver); +}; + +// A WidgetDelegate that supplies an app icon. +class TestWidgetDelegate : public WidgetDelegateView { + public: + explicit TestWidgetDelegate(const SkBitmap& icon) + : app_icon_(gfx::ImageSkia::CreateFrom1xBitmap(icon)) {} + + ~TestWidgetDelegate() override {} + + void SetIcon(const SkBitmap& icon) { + app_icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon); + } + + // views::WidgetDelegate: + gfx::ImageSkia GetWindowAppIcon() override { return app_icon_; } + + private: + gfx::ImageSkia app_icon_; + + DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); +}; + +class NativeWidgetMusTest : public ViewsTestBase { + public: + NativeWidgetMusTest() {} + ~NativeWidgetMusTest() override {} + + // Creates a test widget. Takes ownership of |delegate|. + scoped_ptr<Widget> CreateWidget(TestWidgetDelegate* delegate) { + scoped_ptr<Widget> widget(new Widget()); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.delegate = delegate; + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(10, 20, 100, 200); + widget->Init(params); + return widget; + } + + private: + DISALLOW_COPY_AND_ASSIGN(NativeWidgetMusTest); +}; + +// Tests communication of activation and focus between Widget and +// NativeWidgetMus. +TEST_F(NativeWidgetMusTest, OnActivationChanged) { + scoped_ptr<Widget> widget(CreateWidget(nullptr)); + widget->Show(); + + // Track activation, focus and blur events. + WidgetActivationObserver activation_observer(widget.get()); + TestWidgetFocusChangeListener focus_listener; + WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener); + + // Deactivate the Widget, which deactivates the NativeWidgetMus. + widget->Deactivate(); + + // The widget is blurred and deactivated. + ASSERT_EQ(1u, focus_listener.focus_changes().size()); + EXPECT_EQ(nullptr, focus_listener.focus_changes()[0]); + ASSERT_EQ(1u, activation_observer.changes().size()); + EXPECT_EQ(false, activation_observer.changes()[0]); + + // Re-activate the Widget, which actives the NativeWidgetMus. + widget->Activate(); + + // The widget is focused and activated. + ASSERT_EQ(2u, focus_listener.focus_changes().size()); + EXPECT_EQ(widget->GetNativeView(), focus_listener.focus_changes()[1]); + ASSERT_EQ(2u, activation_observer.changes().size()); + EXPECT_TRUE(activation_observer.changes()[1]); + + WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener); +} + +// Tests that a window with an icon sets the mus::Window icon property. +TEST_F(NativeWidgetMusTest, AppIcon) { + // Create a Widget with a bitmap as the icon. + SkBitmap source_bitmap = MakeBitmap(SK_ColorRED); + scoped_ptr<Widget> widget( + CreateWidget(new TestWidgetDelegate(source_bitmap))); + + // The mus::Window has the icon property. + mus::Window* window = + static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); + EXPECT_TRUE(window->HasSharedProperty( + mus::mojom::WindowManager::kWindowAppIcon_Property)); + + // The icon is the expected icon. + SkBitmap icon = window->GetSharedProperty<SkBitmap>( + mus::mojom::WindowManager::kWindowAppIcon_Property); + EXPECT_TRUE(gfx::BitmapsAreEqual(source_bitmap, icon)); +} + +// Tests that a window without an icon does not set the mus::Window icon +// property. +TEST_F(NativeWidgetMusTest, NoAppIcon) { + // Create a Widget without a special icon. + scoped_ptr<Widget> widget(CreateWidget(nullptr)); + + // The mus::Window does not have an icon property. + mus::Window* window = + static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); + EXPECT_FALSE(window->HasSharedProperty( + mus::mojom::WindowManager::kWindowAppIcon_Property)); +} + +// Tests that changing the icon on a Widget updates the mus::Window icon +// property. +TEST_F(NativeWidgetMusTest, ChangeAppIcon) { + // Create a Widget with an icon. + SkBitmap bitmap1 = MakeBitmap(SK_ColorRED); + TestWidgetDelegate* delegate = new TestWidgetDelegate(bitmap1); + scoped_ptr<Widget> widget(CreateWidget(delegate)); + + // Update the icon to a new image. + SkBitmap bitmap2 = MakeBitmap(SK_ColorGREEN); + delegate->SetIcon(bitmap2); + widget->UpdateWindowIcon(); + + // The window has the updated icon. + mus::Window* window = + static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); + SkBitmap icon = window->GetSharedProperty<SkBitmap>( + mus::mojom::WindowManager::kWindowAppIcon_Property); + EXPECT_TRUE(gfx::BitmapsAreEqual(bitmap2, icon)); +} + +TEST_F(NativeWidgetMusTest, ValidLayerTree) { + scoped_ptr<Widget> widget(CreateWidget(nullptr)); + View* content = new View; + content->SetPaintToLayer(true); + widget->GetContentsView()->AddChildView(content); + EXPECT_TRUE(widget->GetNativeWindow()->layer()->Contains(content->layer())); +} + +} // namespace +} // namespace views diff --git a/chromium/ui/views/mus/platform_test_helper_mus.cc b/chromium/ui/views/mus/platform_test_helper_mus.cc index 7fc3b2286ab..80e6a74a1ec 100644 --- a/chromium/ui/views/mus/platform_test_helper_mus.cc +++ b/chromium/ui/views/mus/platform_test_helper_mus.cc @@ -4,46 +4,97 @@ #include "ui/views/test/platform_test_helper.h" -#include "base/path_service.h" -#include "mojo/shell/public/cpp/application_impl.h" -#include "mojo/shell/public/cpp/application_test_base.h" +#include "base/command_line.h" +#include "base/run_loop.h" +#include "mojo/shell/background/background_shell.h" +#include "mojo/shell/background/tests/test_catalog_store.h" +#include "mojo/shell/public/cpp/connector.h" +#include "mojo/shell/public/cpp/shell_client.h" +#include "mojo/shell/public/cpp/shell_connection.h" #include "ui/aura/env.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/base/ui_base_paths.h" -#include "ui/gl/test/gl_surface_test_support.h" #include "ui/views/mus/window_manager_connection.h" +#include "ui/views/views_delegate.h" + +using mojo::shell::BackgroundShell; namespace views { namespace { +const char kTestName[] = "mojo:test-app"; + +class DefaultShellClient : public mojo::ShellClient { + public: + DefaultShellClient() {} + ~DefaultShellClient() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(DefaultShellClient); +}; + +scoped_ptr<mojo::shell::TestCatalogStore> BuildTestCatalogStore() { + scoped_ptr<base::ListValue> apps(new base::ListValue); + apps->Append( + mojo::shell::BuildPermissiveSerializedAppInfo(kTestName, "test")); + return make_scoped_ptr(new mojo::shell::TestCatalogStore(std::move(apps))); +} + class PlatformTestHelperMus : public PlatformTestHelper { public: PlatformTestHelperMus() { - gfx::GLSurfaceTestSupport::InitializeOneOff(); + background_shell_.reset(new BackgroundShell); + scoped_ptr<BackgroundShell::InitParams> init_params( + new BackgroundShell::InitParams); + init_params->catalog_store = BuildTestCatalogStore(); + background_shell_->Init(std::move(init_params)); + shell_client_.reset(new DefaultShellClient); + shell_connection_.reset(new mojo::ShellConnection( + shell_client_.get(), + background_shell_->CreateShellClientRequest(kTestName))); - // TODO(sky): We really shouldn't need to configure ResourceBundle. - ui::RegisterPathProvider(); - base::FilePath ui_test_pak_path; - CHECK(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); - ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); - aura::Env::CreateInstance(true); + // TODO(rockot): Remove this RunLoop. http://crbug.com/594852. + base::RunLoop wait_loop; + shell_connection_->set_initialize_handler(wait_loop.QuitClosure()); + wait_loop.Run(); - mojo_test_helper_.reset(new mojo::test::TestHelper(nullptr)); // ui/views/mus requires a WindowManager running, for now use the desktop // one. - mojo_test_helper_->application_impl()->ConnectToApplication( - "mojo:desktop_wm"); - WindowManagerConnection::Create(mojo_test_helper_->application_impl()); + mojo::Connector* connector = shell_connection_->connector(); + connector->Connect("mojo:desktop_wm"); + WindowManagerConnection::Create(connector); + + // On X we need to reset the ContextFactory before every NativeWidgetMus + // is created. + // TODO(sad): this is a hack, figure out a better solution. + ViewsDelegate::GetInstance()->set_native_widget_factory(base::Bind( + &PlatformTestHelperMus::CreateNativeWidgetMus, base::Unretained(this), + std::map<std::string, std::vector<uint8_t>>())); } ~PlatformTestHelperMus() override { - mojo_test_helper_.reset(nullptr); - aura::Env::DeleteInstance(); - ui::ResourceBundle::CleanupSharedInstance(); + WindowManagerConnection::Reset(); + // |app_| has a reference to us, destroy it while we are still valid. + shell_connection_.reset(); } + bool IsMus() const override { return true; } + private: - scoped_ptr<mojo::test::TestHelper> mojo_test_helper_; + NativeWidget* CreateNativeWidgetMus( + const std::map<std::string, std::vector<uint8_t>>& props, + const Widget::InitParams& init_params, + internal::NativeWidgetDelegate* delegate) { + ui::ContextFactory* factory = aura::Env::GetInstance()->context_factory(); + aura::Env::GetInstance()->set_context_factory(nullptr); + NativeWidget* result = + WindowManagerConnection::Get()->CreateNativeWidgetMus( + props, init_params, delegate); + aura::Env::GetInstance()->set_context_factory(factory); + return result; + } + + scoped_ptr<BackgroundShell> background_shell_; + scoped_ptr<mojo::ShellConnection> shell_connection_; + scoped_ptr<DefaultShellClient> shell_client_; DISALLOW_COPY_AND_ASSIGN(PlatformTestHelperMus); }; diff --git a/chromium/ui/views/mus/platform_window_mus.cc b/chromium/ui/views/mus/platform_window_mus.cc index 2f29180c893..d42a9a77908 100644 --- a/chromium/ui/views/mus/platform_window_mus.cc +++ b/chromium/ui/views/mus/platform_window_mus.cc @@ -5,10 +5,12 @@ #include "ui/views/mus/platform_window_mus.h" #include "build/build_config.h" +#include "components/bitmap_uploader/bitmap_uploader.h" #include "components/mus/public/cpp/property_type_converters.h" #include "components/mus/public/cpp/window_property.h" #include "components/mus/public/interfaces/window_manager.mojom.h" #include "mojo/converters/input_events/input_events_type_converters.h" +#include "ui/base/view_prop.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/views/mus/window_manager_connection.h" @@ -20,12 +22,12 @@ static uint32_t accelerated_widget_count = 1; } // namespace PlatformWindowMus::PlatformWindowMus(ui::PlatformWindowDelegate* delegate, + mojo::Connector* connector, mus::Window* mus_window) : delegate_(delegate), mus_window_(mus_window), - show_state_(mus::mojom::SHOW_STATE_RESTORED), - last_cursor_(mus::mojom::CURSOR_NULL), - has_capture_(false), + show_state_(mus::mojom::ShowState::RESTORED), + last_cursor_(mus::mojom::Cursor::CURSOR_NULL), mus_window_destroyed_(false) { DCHECK(delegate_); DCHECK(mus_window_); @@ -36,14 +38,20 @@ PlatformWindowMus::PlatformWindowMus(ui::PlatformWindowDelegate* delegate, // window and fit in the smallest sizeof(AcceleratedWidget) uint32_t // has this property. #if defined(OS_WIN) || defined(OS_ANDROID) - delegate_->OnAcceleratedWidgetAvailable( - reinterpret_cast<gfx::AcceleratedWidget>(accelerated_widget_count++), - mus_window_->viewport_metrics().device_pixel_ratio); + gfx::AcceleratedWidget accelerated_widget = + reinterpret_cast<gfx::AcceleratedWidget>(accelerated_widget_count++); #else - delegate_->OnAcceleratedWidgetAvailable( - static_cast<gfx::AcceleratedWidget>(accelerated_widget_count++), - mus_window_->viewport_metrics().device_pixel_ratio); + gfx::AcceleratedWidget accelerated_widget = + static_cast<gfx::AcceleratedWidget>(accelerated_widget_count++); #endif + delegate_->OnAcceleratedWidgetAvailable( + accelerated_widget, mus_window_->viewport_metrics().device_pixel_ratio); + + bitmap_uploader_.reset(new bitmap_uploader::BitmapUploader(mus_window_)); + bitmap_uploader_->Init(connector); + prop_.reset(new ui::ViewProp( + accelerated_widget, bitmap_uploader::kBitmapUploaderForAcceleratedWidget, + bitmap_uploader_.get())); } PlatformWindowMus::~PlatformWindowMus() { @@ -95,15 +103,11 @@ void PlatformWindowMus::SetTitle(const base::string16& title) { } void PlatformWindowMus::SetCapture() { - // TODO(sky): this is wrong, need real capture api. - has_capture_ = true; - NOTIMPLEMENTED(); + mus_window_->SetCapture(); } void PlatformWindowMus::ReleaseCapture() { - // TODO(sky): this is wrong, need real capture api. - has_capture_ = false; - NOTIMPLEMENTED(); + mus_window_->ReleaseCapture(); } void PlatformWindowMus::ToggleFullscreen() { @@ -111,15 +115,15 @@ void PlatformWindowMus::ToggleFullscreen() { } void PlatformWindowMus::Maximize() { - SetShowState(mus::mojom::SHOW_STATE_MAXIMIZED); + SetShowState(mus::mojom::ShowState::MAXIMIZED); } void PlatformWindowMus::Minimize() { - SetShowState(mus::mojom::SHOW_STATE_MINIMIZED); + SetShowState(mus::mojom::ShowState::MINIMIZED); } void PlatformWindowMus::Restore() { - SetShowState(mus::mojom::SHOW_STATE_RESTORED); + SetShowState(mus::mojom::ShowState::RESTORED); } void PlatformWindowMus::SetCursor(ui::PlatformCursor cursor) { @@ -140,7 +144,8 @@ ui::PlatformImeController* PlatformWindowMus::GetPlatformImeController() { void PlatformWindowMus::SetShowState(mus::mojom::ShowState show_state) { mus_window_->SetSharedProperty<int32_t>( - mus::mojom::WindowManager::kShowState_Property, show_state); + mus::mojom::WindowManager::kShowState_Property, + static_cast<int32_t>(show_state)); } void PlatformWindowMus::OnWindowDestroyed(mus::Window* window) { @@ -193,17 +198,17 @@ void PlatformWindowMus::OnWindowSharedPropertyChanged( show_state_ = show_state; ui::PlatformWindowState state = ui::PLATFORM_WINDOW_STATE_UNKNOWN; switch (show_state_) { - case mus::mojom::SHOW_STATE_MINIMIZED: + case mus::mojom::ShowState::MINIMIZED: state = ui::PLATFORM_WINDOW_STATE_MINIMIZED; break; - case mus::mojom::SHOW_STATE_MAXIMIZED: + case mus::mojom::ShowState::MAXIMIZED: state = ui::PLATFORM_WINDOW_STATE_MAXIMIZED; break; - case mus::mojom::SHOW_STATE_RESTORED: + case mus::mojom::ShowState::RESTORED: state = ui::PLATFORM_WINDOW_STATE_NORMAL; break; - case mus::mojom::SHOW_STATE_IMMERSIVE: - case mus::mojom::SHOW_STATE_PRESENTATION: + case mus::mojom::ShowState::IMMERSIVE: + case mus::mojom::ShowState::PRESENTATION: // This may not be sufficient. state = ui::PLATFORM_WINDOW_STATE_FULLSCREEN; break; @@ -217,15 +222,16 @@ void PlatformWindowMus::OnRequestClose(mus::Window* window) { void PlatformWindowMus::OnWindowInputEvent( mus::Window* view, - mus::mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) { + const ui::Event& event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback) { // It's possible dispatching the event will spin a nested message loop. Ack // the callback now, otherwise we appear unresponsive for the life of the // nested message loop. - (*ack_callback)->Run(); + (*ack_callback)->Run(true); ack_callback->reset(); - scoped_ptr<ui::Event> ui_event(event.To<scoped_ptr<ui::Event>>()); - delegate_->DispatchEvent(ui_event.get()); + // TODO(moshayedi): Avoid cloning after updating PlatformWindowDelegate to + // accept constant pointers. + delegate_->DispatchEvent(ui::Event::Clone(event).get()); } } // namespace views diff --git a/chromium/ui/views/mus/platform_window_mus.h b/chromium/ui/views/mus/platform_window_mus.h index fadd64ff5d5..ba206713f08 100644 --- a/chromium/ui/views/mus/platform_window_mus.h +++ b/chromium/ui/views/mus/platform_window_mus.h @@ -16,6 +16,19 @@ #include "ui/platform_window/platform_window.h" #include "ui/views/mus/mus_export.h" +namespace bitmap_uploader { +class BitmapUploader; +} + +namespace mojo { +class Connector; +} + +namespace ui { +class Event; +class ViewProp; +} + namespace views { class VIEWS_MUS_EXPORT PlatformWindowMus @@ -24,6 +37,7 @@ class VIEWS_MUS_EXPORT PlatformWindowMus public NON_EXPORTED_BASE(mus::InputEventHandler) { public: PlatformWindowMus(ui::PlatformWindowDelegate* delegate, + mojo::Connector* connector, mus::Window* mus_window); ~PlatformWindowMus() override; @@ -69,19 +83,21 @@ class VIEWS_MUS_EXPORT PlatformWindowMus void OnRequestClose(mus::Window* window) override; // mus::InputEventHandler: - void OnWindowInputEvent(mus::Window* view, - mus::mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) override; + void OnWindowInputEvent( + mus::Window* view, + const ui::Event& event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback) override; ui::PlatformWindowDelegate* delegate_; mus::Window* mus_window_; mus::mojom::ShowState show_state_; mus::mojom::Cursor last_cursor_; - bool has_capture_; // True if OnWindowDestroyed() has been received. bool mus_window_destroyed_; + scoped_ptr<bitmap_uploader::BitmapUploader> bitmap_uploader_; + scoped_ptr<ui::ViewProp> prop_; #ifndef NDEBUG scoped_ptr<base::WeakPtrFactory<PlatformWindowMus>> weak_factory_; #endif diff --git a/chromium/ui/views/mus/run_all_unittests_mus.cc b/chromium/ui/views/mus/run_all_unittests_mus.cc new file mode 100644 index 00000000000..4de8cdb1839 --- /dev/null +++ b/chromium/ui/views/mus/run_all_unittests_mus.cc @@ -0,0 +1,9 @@ +// 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/views/run_all_unittests.h" + +int MasterProcessMain(int argc, char** argv) { + return views::RunAllUnittests(argc, argv); +} diff --git a/chromium/ui/views/mus/screen_mus.cc b/chromium/ui/views/mus/screen_mus.cc new file mode 100644 index 00000000000..855f475674e --- /dev/null +++ b/chromium/ui/views/mus/screen_mus.cc @@ -0,0 +1,237 @@ +// 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/views/mus/screen_mus.h" + +#include "mojo/converters/geometry/geometry_type_converters.h" +#include "mojo/shell/public/cpp/connection.h" +#include "mojo/shell/public/cpp/connector.h" +#include "ui/gfx/display_finder.h" +#include "ui/gfx/display_observer.h" +#include "ui/views/mus/screen_mus_delegate.h" +#include "ui/views/mus/window_manager_frame_values.h" + +namespace mojo { + +template <> +struct TypeConverter<gfx::Display, mus::mojom::DisplayPtr> { + static gfx::Display Convert(const mus::mojom::DisplayPtr& input) { + gfx::Display result(input->id, input->bounds.To<gfx::Rect>()); + result.set_work_area(input->work_area.To<gfx::Rect>()); + result.set_device_scale_factor(input->device_pixel_ratio); + switch (input->rotation) { + case mus::mojom::Rotation::VALUE_0: + result.set_rotation(gfx::Display::ROTATE_0); + break; + case mus::mojom::Rotation::VALUE_90: + result.set_rotation(gfx::Display::ROTATE_90); + break; + case mus::mojom::Rotation::VALUE_180: + result.set_rotation(gfx::Display::ROTATE_180); + break; + case mus::mojom::Rotation::VALUE_270: + result.set_rotation(gfx::Display::ROTATE_270); + break; + } + switch (input->touch_support) { + case mus::mojom::TouchSupport::UNKNOWN: + result.set_touch_support(gfx::Display::TOUCH_SUPPORT_UNKNOWN); + break; + case mus::mojom::TouchSupport::AVAILABLE: + result.set_touch_support(gfx::Display::TOUCH_SUPPORT_AVAILABLE); + break; + case mus::mojom::TouchSupport::UNAVAILABLE: + result.set_touch_support(gfx::Display::TOUCH_SUPPORT_UNAVAILABLE); + break; + } + return result; + } +}; + +template <> +struct TypeConverter<views::WindowManagerFrameValues, + mus::mojom::FrameDecorationValuesPtr> { + static views::WindowManagerFrameValues Convert( + const mus::mojom::FrameDecorationValuesPtr& input) { + views::WindowManagerFrameValues result; + result.normal_insets = input->normal_client_area_insets.To<gfx::Insets>(); + result.maximized_insets = + input->maximized_client_area_insets.To<gfx::Insets>(); + result.max_title_bar_button_width = input->max_title_bar_button_width; + return result; + } +}; + +} // namespace mojo + +namespace views { + +ScreenMus::ScreenMus(ScreenMusDelegate* delegate) + : delegate_(delegate), + primary_display_index_(0), + display_manager_observer_binding_(this) {} + +ScreenMus::~ScreenMus() {} + +void ScreenMus::Init(mojo::Connector* connector) { + gfx::Screen::SetScreenInstance(this); + + connector->ConnectToInterface("mojo:mus", &display_manager_); + + display_manager_->AddObserver( + display_manager_observer_binding_.CreateInterfacePtrAndBind()); + + // We need the set of displays before we can continue. Wait for it. + // + // TODO(rockot): Do something better here. This should not have to block tasks + // from running on the calling thread. http://crbug.com/594852. + display_manager_observer_binding_.WaitForIncomingMethodCall(); + + // The WaitForIncomingMethodCall() should have supplied the set of Displays. + DCHECK(displays_.size()); +} + +int ScreenMus::FindDisplayIndexById(int64_t id) const { + for (size_t i = 0; i < displays_.size(); ++i) { + if (displays_[i].id() == id) + return static_cast<int>(i); + } + return -1; +} + +void ScreenMus::ProcessDisplayChanged(const gfx::Display& changed_display, + bool is_primary) { + const int display_index = FindDisplayIndexById(changed_display.id()); + if (display_index == -1) { + displays_.push_back(changed_display); + if (is_primary) + primary_display_index_ = static_cast<int>(displays_.size()) - 1; + FOR_EACH_OBSERVER(gfx::DisplayObserver, observers_, + OnDisplayAdded(changed_display)); + return; + } + + gfx::Display* local_display = &displays_[display_index]; + uint32_t changed_values = 0; + if (is_primary && display_index != primary_display_index_) { + primary_display_index_ = display_index; + // ash::DisplayManager only notifies for the Display gaining primary, not + // the one losing it. + changed_values |= gfx::DisplayObserver::DISPLAY_METRIC_PRIMARY; + } + if (local_display->bounds() != changed_display.bounds()) { + local_display->set_bounds(changed_display.bounds()); + changed_values |= gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS; + } + if (local_display->work_area() != changed_display.work_area()) { + local_display->set_work_area(changed_display.work_area()); + changed_values |= gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA; + } + if (local_display->rotation() != changed_display.rotation()) { + local_display->set_rotation(changed_display.rotation()); + changed_values |= gfx::DisplayObserver::DISPLAY_METRIC_ROTATION; + } + if (local_display->device_scale_factor() != + changed_display.device_scale_factor()) { + local_display->set_device_scale_factor( + changed_display.device_scale_factor()); + changed_values |= gfx::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; + } + FOR_EACH_OBSERVER(gfx::DisplayObserver, observers_, + OnDisplayMetricsChanged(*local_display, changed_values)); +} + +gfx::Point ScreenMus::GetCursorScreenPoint() { + NOTIMPLEMENTED(); + return gfx::Point(); +} + +gfx::NativeWindow ScreenMus::GetWindowUnderCursor() { + NOTIMPLEMENTED(); + return nullptr; +} + +gfx::NativeWindow ScreenMus::GetWindowAtScreenPoint(const gfx::Point& point) { + NOTIMPLEMENTED(); + return nullptr; +} + +gfx::Display ScreenMus::GetPrimaryDisplay() const { + return displays_[primary_display_index_]; +} + +gfx::Display ScreenMus::GetDisplayNearestWindow(gfx::NativeView view) const { + //NOTIMPLEMENTED(); + return GetPrimaryDisplay(); +} + +gfx::Display ScreenMus::GetDisplayNearestPoint(const gfx::Point& point) const { + return *gfx::FindDisplayNearestPoint(displays_, point); +} + +int ScreenMus::GetNumDisplays() const { + return static_cast<int>(displays_.size()); +} + +std::vector<gfx::Display> ScreenMus::GetAllDisplays() const { + return displays_; +} + +gfx::Display ScreenMus::GetDisplayMatching(const gfx::Rect& match_rect) const { + const gfx::Display* match = + gfx::FindDisplayWithBiggestIntersection(displays_, match_rect); + return match ? *match : GetPrimaryDisplay(); +} + +void ScreenMus::AddObserver(gfx::DisplayObserver* observer) { + observers_.AddObserver(observer); +} + +void ScreenMus::RemoveObserver(gfx::DisplayObserver* observer) { + observers_.RemoveObserver(observer); +} + +void ScreenMus::OnDisplays(mojo::Array<mus::mojom::DisplayPtr> displays) { + // This should only be called once from Init() before any observers have been + // added. + DCHECK(displays_.empty()); + displays_ = displays.To<std::vector<gfx::Display>>(); + for (size_t i = 0; i < displays.size(); ++i) { + if (displays[i]->is_primary) { + primary_display_index_ = static_cast<int>(i); + // TODO(sky): Make WindowManagerFrameValues per display. + WindowManagerFrameValues frame_values = + displays[i]->frame_decoration_values.To<WindowManagerFrameValues>(); + WindowManagerFrameValues::SetInstance(frame_values); + } + } +} + +void ScreenMus::OnDisplaysChanged( + mojo::Array<mus::mojom::DisplayPtr> transport_displays) { + for (size_t i = 0; i < transport_displays.size(); ++i) { + const bool is_primary = transport_displays[i]->is_primary; + ProcessDisplayChanged(transport_displays[i].To<gfx::Display>(), is_primary); + if (is_primary) { + WindowManagerFrameValues frame_values = + transport_displays[i] + ->frame_decoration_values.To<WindowManagerFrameValues>(); + WindowManagerFrameValues::SetInstance(frame_values); + delegate_->OnWindowManagerFrameValuesChanged(); + } + } +} + +void ScreenMus::OnDisplayRemoved(int64_t id) { + const int index = FindDisplayIndexById(id); + DCHECK_NE(-1, index); + // Another display must become primary before the existing primary is + // removed. + DCHECK_NE(index, primary_display_index_); + const gfx::Display display = displays_[index]; + FOR_EACH_OBSERVER(gfx::DisplayObserver, observers_, + OnDisplayRemoved(display)); +} + +} // namespace views diff --git a/chromium/ui/views/mus/screen_mus.h b/chromium/ui/views/mus/screen_mus.h new file mode 100644 index 00000000000..a6800afd72e --- /dev/null +++ b/chromium/ui/views/mus/screen_mus.h @@ -0,0 +1,75 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_MUS_SCREEN_MUS_H_ +#define UI_VIEWS_MUS_SCREEN_MUS_H_ + +#include <vector> + +#include "base/observer_list.h" +#include "base/run_loop.h" +#include "components/mus/public/interfaces/display.mojom.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" +#include "ui/views/mus/mus_export.h" + +namespace mojo { +class Connector; +} + +namespace views { + +class ScreenMusDelegate; + +// Screen implementation backed by mus::mojom::DisplayManager. +class VIEWS_MUS_EXPORT ScreenMus + : public gfx::Screen, + public NON_EXPORTED_BASE(mus::mojom::DisplayManagerObserver) { + public: + explicit ScreenMus(ScreenMusDelegate* delegate); + ~ScreenMus() override; + + void Init(mojo::Connector* connector); + + private: + int FindDisplayIndexById(int64_t id) const; + + // Invoked when a display changed in some weay, including being added. + // If |is_primary| is true, |changed_display| is the primary display. + void ProcessDisplayChanged(const gfx::Display& changed_display, + bool is_primary); + + // gfx::Screen: + gfx::Point GetCursorScreenPoint() override; + gfx::NativeWindow GetWindowUnderCursor() override; + gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override; + gfx::Display GetPrimaryDisplay() const override; + gfx::Display GetDisplayNearestWindow(gfx::NativeView view) const override; + gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override; + int GetNumDisplays() const override; + std::vector<gfx::Display> GetAllDisplays() const override; + gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override; + void AddObserver(gfx::DisplayObserver* observer) override; + void RemoveObserver(gfx::DisplayObserver* observer) override; + + // mus::mojom::DisplayManager: + void OnDisplays(mojo::Array<mus::mojom::DisplayPtr> displays) override; + void OnDisplaysChanged(mojo::Array<mus::mojom::DisplayPtr> display) override; + void OnDisplayRemoved(int64_t id) override; + + ScreenMusDelegate* delegate_; + mus::mojom::DisplayManagerPtr display_manager_; + std::vector<gfx::Display> displays_; + int primary_display_index_; + mojo::Binding<mus::mojom::DisplayManagerObserver> + display_manager_observer_binding_; + base::ObserverList<gfx::DisplayObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(ScreenMus); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_SCREEN_MUS_H_ diff --git a/chromium/ui/views/mus/screen_mus_delegate.h b/chromium/ui/views/mus/screen_mus_delegate.h new file mode 100644 index 00000000000..4c7940088c5 --- /dev/null +++ b/chromium/ui/views/mus/screen_mus_delegate.h @@ -0,0 +1,23 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_MUS_SCREEN_MUS_DELEGATE_H_ +#define UI_VIEWS_MUS_SCREEN_MUS_DELEGATE_H_ + +#include "ui/views/mus/mus_export.h" + +namespace views { + +// Screen implementation backed by mus::mojom::DisplayManager. +class VIEWS_MUS_EXPORT ScreenMusDelegate { + public: + virtual void OnWindowManagerFrameValuesChanged() = 0; + + protected: + virtual ~ScreenMusDelegate() {} +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_SCREEN_MUS_DELEGATE_H_ diff --git a/chromium/ui/views/mus/surface_binding.cc b/chromium/ui/views/mus/surface_binding.cc index ed4aedff436..e33e3247bd7 100644 --- a/chromium/ui/views/mus/surface_binding.cc +++ b/chromium/ui/views/mus/surface_binding.cc @@ -26,15 +26,10 @@ #include "mojo/converters/geometry/geometry_type_converters.h" #include "mojo/converters/surfaces/surfaces_type_converters.h" #include "mojo/public/cpp/bindings/binding.h" -#include "mojo/shell/public/cpp/application_impl.h" -#include "mojo/shell/public/cpp/connect.h" -#include "mojo/shell/public/interfaces/shell.mojom.h" +#include "mojo/shell/public/cpp/connector.h" #include "ui/views/mus/window_tree_host_mus.h" namespace views { -namespace { -void OnGotContentHandlerID(uint32_t content_handler_id) {} -} // namespace // PerConnectionState ---------------------------------------------------------- @@ -46,7 +41,7 @@ void OnGotContentHandlerID(uint32_t content_handler_id) {} class SurfaceBinding::PerConnectionState : public base::RefCounted<PerConnectionState> { public: - static PerConnectionState* Get(mojo::Shell* shell, + static PerConnectionState* Get(mojo::Connector* connector, mus::WindowTreeConnection* connection); scoped_ptr<cc::OutputSurface> CreateOutputSurface( @@ -59,7 +54,8 @@ class SurfaceBinding::PerConnectionState friend class base::RefCounted<PerConnectionState>; - PerConnectionState(mojo::Shell* shell, mus::WindowTreeConnection* connection); + PerConnectionState(mojo::Connector* connector, + mus::WindowTreeConnection* connection); ~PerConnectionState(); void Init(); @@ -67,7 +63,7 @@ class SurfaceBinding::PerConnectionState static base::LazyInstance< base::ThreadLocalPointer<ConnectionToStateMap>>::Leaky window_states; - mojo::Shell* shell_; + mojo::Connector* connector_; mus::WindowTreeConnection* connection_; // Set of state needed to create an OutputSurface. @@ -83,7 +79,7 @@ base::LazyInstance<base::ThreadLocalPointer< // static SurfaceBinding::PerConnectionState* SurfaceBinding::PerConnectionState::Get( - mojo::Shell* shell, + mojo::Connector* connector, mus::WindowTreeConnection* connection) { ConnectionToStateMap* window_map = window_states.Pointer()->Get(); if (!window_map) { @@ -91,7 +87,7 @@ SurfaceBinding::PerConnectionState* SurfaceBinding::PerConnectionState::Get( window_states.Pointer()->Set(window_map); } if (!(*window_map)[connection]) { - (*window_map)[connection] = new PerConnectionState(shell, connection); + (*window_map)[connection] = new PerConnectionState(connector, connection); (*window_map)[connection]->Init(); } return (*window_map)[connection]; @@ -113,9 +109,9 @@ SurfaceBinding::PerConnectionState::CreateOutputSurface( } SurfaceBinding::PerConnectionState::PerConnectionState( - mojo::Shell* shell, + mojo::Connector* connector, mus::WindowTreeConnection* connection) - : shell_(shell), connection_(connection) {} + : connector_(connector), connection_(connection) {} SurfaceBinding::PerConnectionState::~PerConnectionState() { ConnectionToStateMap* window_map = window_states.Pointer()->Get(); @@ -129,24 +125,17 @@ SurfaceBinding::PerConnectionState::~PerConnectionState() { } void SurfaceBinding::PerConnectionState::Init() { - mojo::ServiceProviderPtr service_provider; - mojo::URLRequestPtr request(mojo::URLRequest::New()); - request->url = mojo::String::From("mojo:mus"); - shell_->ConnectToApplication(std::move(request), GetProxy(&service_provider), - nullptr, - mojo::CreatePermissiveCapabilityFilter(), - base::Bind(&OnGotContentHandlerID)); - ConnectToService(service_provider.get(), &gpu_); + connector_->ConnectToInterface("mojo:mus", &gpu_); } // SurfaceBinding -------------------------------------------------------------- -SurfaceBinding::SurfaceBinding(mojo::Shell* shell, +SurfaceBinding::SurfaceBinding(mojo::Connector* connector, mus::Window* window, mus::mojom::SurfaceType surface_type) : window_(window), surface_type_(surface_type), - state_(PerConnectionState::Get(shell, window->connection())) {} + state_(PerConnectionState::Get(connector, window->connection())) {} SurfaceBinding::~SurfaceBinding() {} diff --git a/chromium/ui/views/mus/surface_binding.h b/chromium/ui/views/mus/surface_binding.h index 0b468b574e4..2c6c5b68a33 100644 --- a/chromium/ui/views/mus/surface_binding.h +++ b/chromium/ui/views/mus/surface_binding.h @@ -16,7 +16,7 @@ class OutputSurface; } namespace mojo { -class Shell; +class Connector; } namespace mus { @@ -32,7 +32,7 @@ namespace views { // connection. class VIEWS_MUS_EXPORT SurfaceBinding { public: - SurfaceBinding(mojo::Shell* shell, + SurfaceBinding(mojo::Connector* connector, mus::Window* window, mus::mojom::SurfaceType surface_type); ~SurfaceBinding(); diff --git a/chromium/ui/views/mus/surface_context_factory.cc b/chromium/ui/views/mus/surface_context_factory.cc index 0d5bfeea57e..bb27a08fb06 100644 --- a/chromium/ui/views/mus/surface_context_factory.cc +++ b/chromium/ui/views/mus/surface_context_factory.cc @@ -8,7 +8,7 @@ #include "cc/resources/shared_bitmap_manager.h" #include "cc/surfaces/surface_id_allocator.h" #include "components/mus/public/cpp/window.h" -#include "mojo/shell/public/interfaces/shell.mojom.h" +#include "mojo/shell/public/interfaces/connector.mojom.h" #include "ui/compositor/reflector.h" #include "ui/gl/gl_bindings.h" @@ -27,10 +27,10 @@ class FakeReflector : public ui::Reflector { } // namespace SurfaceContextFactory::SurfaceContextFactory( - mojo::Shell* shell, + mojo::Connector* connector, mus::Window* window, mus::mojom::SurfaceType surface_type) - : surface_binding_(shell, window, surface_type), + : surface_binding_(connector, window, surface_type), next_surface_id_namespace_(1u) {} SurfaceContextFactory::~SurfaceContextFactory() {} diff --git a/chromium/ui/views/mus/surface_context_factory.h b/chromium/ui/views/mus/surface_context_factory.h index 72c85fde9c0..6399d5a011b 100644 --- a/chromium/ui/views/mus/surface_context_factory.h +++ b/chromium/ui/views/mus/surface_context_factory.h @@ -16,7 +16,7 @@ #include "ui/views/mus/surface_binding.h" namespace mojo { -class Shell; +class Connector; } namespace mus { @@ -27,7 +27,7 @@ namespace views { class VIEWS_MUS_EXPORT SurfaceContextFactory : public ui::ContextFactory { public: - SurfaceContextFactory(mojo::Shell* shell, + SurfaceContextFactory(mojo::Connector* connector, mus::Window* window, mus::mojom::SurfaceType surface_type); ~SurfaceContextFactory() override; diff --git a/chromium/ui/views/mus/window_manager_connection.cc b/chromium/ui/views/mus/window_manager_connection.cc index 286534714f1..fc41913602f 100644 --- a/chromium/ui/views/mus/window_manager_connection.cc +++ b/chromium/ui/views/mus/window_manager_connection.cc @@ -8,62 +8,16 @@ #include "base/lazy_instance.h" #include "base/threading/thread_local.h" -#include "base/threading/thread_restrictions.h" #include "components/mus/public/cpp/window_tree_connection.h" #include "components/mus/public/interfaces/window_tree.mojom.h" #include "mojo/converters/geometry/geometry_type_converters.h" -#include "mojo/converters/network/network_type_converters.h" -#include "mojo/shell/public/cpp/application_connection.h" -#include "mojo/shell/public/cpp/application_impl.h" -#include "ui/gfx/display.h" -#include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/mojo/init/ui_init.h" +#include "mojo/shell/public/cpp/connection.h" +#include "mojo/shell/public/cpp/connector.h" #include "ui/views/mus/native_widget_mus.h" -#include "ui/views/mus/window_manager_frame_values.h" +#include "ui/views/mus/screen_mus.h" #include "ui/views/views_delegate.h" -namespace mojo { - -gfx::Display::Rotation GFXRotationFromMojomRotation( - mus::mojom::Rotation input) { - switch (input) { - case mus::mojom::ROTATION_VALUE_0: - return gfx::Display::ROTATE_0; - case mus::mojom::ROTATION_VALUE_90: - return gfx::Display::ROTATE_90; - case mus::mojom::ROTATION_VALUE_180: - return gfx::Display::ROTATE_180; - case mus::mojom::ROTATION_VALUE_270: - return gfx::Display::ROTATE_270; - } - return gfx::Display::ROTATE_0; -} - -template <> -struct TypeConverter<gfx::Display, mus::mojom::DisplayPtr> { - static gfx::Display Convert(const mus::mojom::DisplayPtr& input) { - gfx::Display result; - result.set_id(input->id); - result.SetScaleAndBounds(input->device_pixel_ratio, - input->bounds.To<gfx::Rect>()); - gfx::Rect work_area( - gfx::ScaleToFlooredPoint( - gfx::Point(input->work_area->x, input->work_area->y), - 1.0f / input->device_pixel_ratio), - gfx::ScaleToFlooredSize( - gfx::Size(input->work_area->width, input->work_area->height), - 1.0f / input->device_pixel_ratio)); - result.set_work_area(work_area); - result.set_rotation(GFXRotationFromMojomRotation(input->rotation)); - return result; - } -}; - -} // namespace mojo - namespace views { - namespace { using WindowManagerConnectionPtr = @@ -73,32 +27,12 @@ using WindowManagerConnectionPtr = base::LazyInstance<WindowManagerConnectionPtr>::Leaky lazy_tls_ptr = LAZY_INSTANCE_INITIALIZER; -std::vector<gfx::Display> GetDisplaysFromWindowManager( - mus::mojom::WindowManagerPtr* window_manager) { - WindowManagerFrameValues frame_values; - std::vector<gfx::Display> displays; - (*window_manager) - ->GetConfig([&displays, - &frame_values](mus::mojom::WindowManagerConfigPtr results) { - displays = results->displays.To<std::vector<gfx::Display>>(); - frame_values.normal_insets = - results->normal_client_area_insets.To<gfx::Insets>(); - frame_values.maximized_insets = - results->maximized_client_area_insets.To<gfx::Insets>(); - frame_values.max_title_bar_button_width = - results->max_title_bar_button_width; - }); - CHECK(window_manager->WaitForIncomingResponse()); - WindowManagerFrameValues::SetInstance(frame_values); - return displays; -} - } // namespace // static -void WindowManagerConnection::Create(mojo::ApplicationImpl* app) { +void WindowManagerConnection::Create(mojo::Connector* connector) { DCHECK(!lazy_tls_ptr.Pointer()->Get()); - lazy_tls_ptr.Pointer()->Set(new WindowManagerConnection(app)); + lazy_tls_ptr.Pointer()->Set(new WindowManagerConnection(connector)); } // static @@ -108,36 +42,44 @@ WindowManagerConnection* WindowManagerConnection::Get() { return connection; } +// static +bool WindowManagerConnection::Exists() { + return !!lazy_tls_ptr.Pointer()->Get(); +} + +// static +void WindowManagerConnection::Reset() { + delete Get(); + lazy_tls_ptr.Pointer()->Set(nullptr); +} + mus::Window* WindowManagerConnection::NewWindow( const std::map<std::string, std::vector<uint8_t>>& properties) { - if (window_tree_connection_) - return window_tree_connection_->NewTopLevelWindow(&properties); - - mus::mojom::WindowTreeClientPtr window_tree_client; - mojo::InterfaceRequest<mus::mojom::WindowTreeClient> - window_tree_client_request = GetProxy(&window_tree_client); - window_manager_->OpenWindow( - std::move(window_tree_client), - mojo::Map<mojo::String, mojo::Array<uint8_t>>::From(properties)); - - base::ThreadRestrictions::ScopedAllowWait allow_wait; - window_tree_connection_.reset(mus::WindowTreeConnection::Create( - this, std::move(window_tree_client_request), - mus::WindowTreeConnection::CreateType::WAIT_FOR_EMBED)); - window_tree_connection_->SetDeleteOnNoRoots(false); - DCHECK_EQ(1u, window_tree_connection_->GetRoots().size()); - return *window_tree_connection_->GetRoots().begin(); + return window_tree_connection_->NewTopLevelWindow(&properties); +} + +NativeWidget* WindowManagerConnection::CreateNativeWidgetMus( + const std::map<std::string, std::vector<uint8_t>>& props, + const Widget::InitParams& init_params, + internal::NativeWidgetDelegate* delegate) { + std::map<std::string, std::vector<uint8_t>> properties = props; + NativeWidgetMus::ConfigurePropertiesForNewWindow(init_params, &properties); + return new NativeWidgetMus(delegate, connector_, NewWindow(properties), + mus::mojom::SurfaceType::DEFAULT); } -WindowManagerConnection::WindowManagerConnection(mojo::ApplicationImpl* app) - : app_(app), window_tree_connection_(nullptr) { - app->ConnectToService("mojo:mus", &window_manager_); +WindowManagerConnection::WindowManagerConnection(mojo::Connector* connector) + : connector_(connector), window_tree_connection_(nullptr) { + window_tree_connection_.reset( + mus::WindowTreeConnection::Create(this, connector_)); - ui_init_.reset(new ui::mojo::UIInit( - GetDisplaysFromWindowManager(&window_manager_))); - ViewsDelegate::GetInstance()->set_native_widget_factory( - base::Bind(&WindowManagerConnection::CreateNativeWidget, - base::Unretained(this))); + screen_.reset(new ScreenMus(this)); + screen_->Init(connector); + + ViewsDelegate::GetInstance()->set_native_widget_factory(base::Bind( + &WindowManagerConnection::CreateNativeWidgetMus, + base::Unretained(this), + std::map<std::string, std::vector<uint8_t>>())); } WindowManagerConnection::~WindowManagerConnection() { @@ -151,13 +93,9 @@ void WindowManagerConnection::OnEmbed(mus::Window* root) {} void WindowManagerConnection::OnConnectionLost( mus::WindowTreeConnection* connection) {} -NativeWidget* WindowManagerConnection::CreateNativeWidget( - const Widget::InitParams& init_params, - internal::NativeWidgetDelegate* delegate) { - std::map<std::string, std::vector<uint8_t>> properties; - NativeWidgetMus::ConfigurePropertiesForNewWindow(init_params, &properties); - return new NativeWidgetMus(delegate, app_->shell(), NewWindow(properties), - mus::mojom::SURFACE_TYPE_DEFAULT); +void WindowManagerConnection::OnWindowManagerFrameValuesChanged() { + if (window_tree_connection_) + NativeWidgetMus::NotifyFrameChanged(window_tree_connection_.get()); } } // namespace views diff --git a/chromium/ui/views/mus/window_manager_connection.h b/chromium/ui/views/mus/window_manager_connection.h index ab12a03b05b..69059893346 100644 --- a/chromium/ui/views/mus/window_manager_connection.h +++ b/chromium/ui/views/mus/window_manager_connection.h @@ -10,57 +10,63 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "components/mus/public/cpp/window_tree_delegate.h" -#include "components/mus/public/interfaces/window_manager.mojom.h" #include "ui/views/mus/mus_export.h" +#include "ui/views/mus/screen_mus_delegate.h" #include "ui/views/widget/widget.h" namespace mojo { -class ApplicationImpl; -} - -namespace ui { -namespace mojo { -class UIInit; -} +class Connector; } namespace views { class NativeWidget; +class ScreenMus; namespace internal { class NativeWidgetDelegate; } -// Establishes a connection to the window manager for use by views within an -// application, and performs Aura initialization. +// Provides configuration to mus in views. This consists of the following: +// . Provides a Screen implementation backed by mus. +// . Creates and owns a WindowTreeConnection. +// . Registers itself as the factory for creating NativeWidgets so that a +// NativeWidgetMus is created. +// WindowManagerConnection is a singleton and should be created early on. +// +// TODO(sky): this name is now totally confusing. Come up with a better one. class VIEWS_MUS_EXPORT WindowManagerConnection - : public NON_EXPORTED_BASE(mus::WindowTreeDelegate) { + : public NON_EXPORTED_BASE(mus::WindowTreeDelegate), + public ScreenMusDelegate { public: - static void Create(mojo::ApplicationImpl* app); + static void Create(mojo::Connector* connector); static WindowManagerConnection* Get(); + static bool Exists(); + + // Destroys the singleton instance. + static void Reset(); - mojo::ApplicationImpl* app() { return app_; } + mojo::Connector* connector() { return connector_; } mus::Window* NewWindow(const std::map<std::string, std::vector<uint8_t>>& properties); - mus::mojom::WindowManager* window_manager() { - return window_manager_.get(); - } + NativeWidget* CreateNativeWidgetMus( + const std::map<std::string, std::vector<uint8_t>>& properties, + const Widget::InitParams& init_params, + internal::NativeWidgetDelegate* delegate); private: - explicit WindowManagerConnection(mojo::ApplicationImpl* app); + explicit WindowManagerConnection(mojo::Connector* connector); ~WindowManagerConnection() override; // mus::WindowTreeDelegate: void OnEmbed(mus::Window* root) override; void OnConnectionLost(mus::WindowTreeConnection* connection) override; - NativeWidget* CreateNativeWidget(const Widget::InitParams& init_params, - internal::NativeWidgetDelegate* delegate); + // ScreenMusDelegate: + void OnWindowManagerFrameValuesChanged() override; - mojo::ApplicationImpl* app_; - mus::mojom::WindowManagerPtr window_manager_; - scoped_ptr<ui::mojo::UIInit> ui_init_; + mojo::Connector* connector_; + scoped_ptr<ScreenMus> screen_; scoped_ptr<mus::WindowTreeConnection> window_tree_connection_; DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection); diff --git a/chromium/ui/views/mus/window_manager_constants_converters.cc b/chromium/ui/views/mus/window_manager_constants_converters.cc index 80e6d1f68be..889737698f1 100644 --- a/chromium/ui/views/mus/window_manager_constants_converters.cc +++ b/chromium/ui/views/mus/window_manager_constants_converters.cc @@ -12,25 +12,25 @@ TypeConverter<mus::mojom::WindowType, views::Widget::InitParams::Type>::Convert( views::Widget::InitParams::Type type) { switch (type) { case views::Widget::InitParams::TYPE_WINDOW: - return mus::mojom::WINDOW_TYPE_WINDOW; + return mus::mojom::WindowType::WINDOW; case views::Widget::InitParams::TYPE_PANEL: - return mus::mojom::WINDOW_TYPE_PANEL; + return mus::mojom::WindowType::PANEL; case views::Widget::InitParams::TYPE_WINDOW_FRAMELESS: - return mus::mojom::WINDOW_TYPE_WINDOW_FRAMELESS; + return mus::mojom::WindowType::WINDOW_FRAMELESS; case views::Widget::InitParams::TYPE_CONTROL: - return mus::mojom::WINDOW_TYPE_CONTROL; + return mus::mojom::WindowType::CONTROL; case views::Widget::InitParams::TYPE_POPUP: - return mus::mojom::WINDOW_TYPE_POPUP; + return mus::mojom::WindowType::POPUP; case views::Widget::InitParams::TYPE_MENU: - return mus::mojom::WINDOW_TYPE_MENU; + return mus::mojom::WindowType::MENU; case views::Widget::InitParams::TYPE_TOOLTIP: - return mus::mojom::WINDOW_TYPE_TOOLTIP; + return mus::mojom::WindowType::TOOLTIP; case views::Widget::InitParams::TYPE_BUBBLE: - return mus::mojom::WINDOW_TYPE_BUBBLE; + return mus::mojom::WindowType::BUBBLE; case views::Widget::InitParams::TYPE_DRAG: - return mus::mojom::WINDOW_TYPE_DRAG; + return mus::mojom::WindowType::DRAG; } - return mus::mojom::WINDOW_TYPE_POPUP; + return mus::mojom::WindowType::POPUP; } } // namespace mojo diff --git a/chromium/ui/views/mus/window_manager_frame_values.h b/chromium/ui/views/mus/window_manager_frame_values.h index b0b8e10ee77..50c73f34c0f 100644 --- a/chromium/ui/views/mus/window_manager_frame_values.h +++ b/chromium/ui/views/mus/window_manager_frame_values.h @@ -19,6 +19,16 @@ struct VIEWS_MUS_EXPORT WindowManagerFrameValues { static void SetInstance(const WindowManagerFrameValues& values); static const WindowManagerFrameValues& instance(); + bool operator==(const WindowManagerFrameValues& other) const { + return normal_insets == other.normal_insets && + maximized_insets == other.maximized_insets && + max_title_bar_button_width == other.max_title_bar_button_width; + } + + bool operator!=(const WindowManagerFrameValues& other) const { + return !(*this == other); + } + // Ideal insets the window manager renders non-client frame decorations into. gfx::Insets normal_insets; gfx::Insets maximized_insets; diff --git a/chromium/ui/views/mus/window_tree_host_mus.cc b/chromium/ui/views/mus/window_tree_host_mus.cc index 6edb53d08aa..2880c0bf480 100644 --- a/chromium/ui/views/mus/window_tree_host_mus.cc +++ b/chromium/ui/views/mus/window_tree_host_mus.cc @@ -4,11 +4,8 @@ #include "ui/views/mus/window_tree_host_mus.h" -#include "components/bitmap_uploader/bitmap_uploader.h" -#include "mojo/shell/public/interfaces/shell.mojom.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" -#include "ui/base/view_prop.h" #include "ui/events/event.h" #include "ui/views/mus/input_method_mus.h" #include "ui/views/mus/native_widget_mus.h" @@ -19,13 +16,13 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // WindowTreeHostMus, public: -WindowTreeHostMus::WindowTreeHostMus(mojo::Shell* shell, +WindowTreeHostMus::WindowTreeHostMus(mojo::Connector* connector, NativeWidgetMus* native_widget, - mus::Window* window, - mus::mojom::SurfaceType surface_type) + mus::Window* window) : native_widget_(native_widget), show_state_(ui::PLATFORM_WINDOW_STATE_UNKNOWN) { - SetPlatformWindow(make_scoped_ptr(new PlatformWindowMus(this, window))); + SetPlatformWindow( + make_scoped_ptr(new PlatformWindowMus(this, connector, window))); // The location of events is already transformed, and there is no way to // correctly determine the reverse transform. So, don't attempt to transform // event locations, else the root location is wrong. @@ -33,13 +30,6 @@ WindowTreeHostMus::WindowTreeHostMus(mojo::Shell* shell, dispatcher()->set_transform_events(false); compositor()->SetHostHasTransparentBackground(true); - bitmap_uploader_.reset(new bitmap_uploader::BitmapUploader(window)); - bitmap_uploader_->Init(shell); - prop_.reset( - new ui::ViewProp(GetAcceleratedWidget(), - bitmap_uploader::kBitmapUploaderForAcceleratedWidget, - bitmap_uploader_.get())); - input_method_.reset(new InputMethodMUS(this, window)); SetSharedInputMethod(input_method_.get()); } @@ -56,7 +46,7 @@ PlatformWindowMus* WindowTreeHostMus::platform_window() { void WindowTreeHostMus::DispatchEvent(ui::Event* event) { if (event->IsKeyEvent() && GetInputMethod()) { - GetInputMethod()->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event)); + GetInputMethod()->DispatchKeyEvent(event->AsKeyEvent()); event->StopPropagation(); return; } diff --git a/chromium/ui/views/mus/window_tree_host_mus.h b/chromium/ui/views/mus/window_tree_host_mus.h index 0116e60b942..0bffe25ea13 100644 --- a/chromium/ui/views/mus/window_tree_host_mus.h +++ b/chromium/ui/views/mus/window_tree_host_mus.h @@ -6,29 +6,19 @@ #define UI_VIEWS_MUS_WINDOW_TREE_HOST_MUS_H_ #include "base/macros.h" -#include "components/mus/public/interfaces/window_tree.mojom.h" #include "ui/aura/window_tree_host_platform.h" #include "ui/views/mus/mus_export.h" class SkBitmap; -namespace bitmap_uploader { -class BitmapUploader; -} - namespace mojo { -class Shell; +class Connector; } namespace mus { class Window; } -namespace ui { -class Compositor; -class ViewProp; -} - namespace views { class InputMethodMUS; @@ -37,16 +27,12 @@ class PlatformWindowMus; class VIEWS_MUS_EXPORT WindowTreeHostMus : public aura::WindowTreeHostPlatform { public: - WindowTreeHostMus(mojo::Shell* shell, - NativeWidgetMus* native_widget_, - mus::Window* window, - mus::mojom::SurfaceType surface_type); + WindowTreeHostMus(mojo::Connector* connector, + NativeWidgetMus* native_widget, + mus::Window* window); ~WindowTreeHostMus() override; PlatformWindowMus* platform_window(); - bitmap_uploader::BitmapUploader* bitmap_uploader() { - return bitmap_uploader_.get(); - } ui::PlatformWindowState show_state() const { return show_state_; } private: @@ -60,8 +46,6 @@ class VIEWS_MUS_EXPORT WindowTreeHostMus : public aura::WindowTreeHostPlatform { NativeWidgetMus* native_widget_; scoped_ptr<InputMethodMUS> input_method_; ui::PlatformWindowState show_state_; - scoped_ptr<bitmap_uploader::BitmapUploader> bitmap_uploader_; - scoped_ptr<ui::ViewProp> prop_; DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMus); }; diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc index 0cc1f0d38c5..9a677956f84 100644 --- a/chromium/ui/views/painter.cc +++ b/chromium/ui/views/painter.cc @@ -12,7 +12,9 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_f.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_operations.h" @@ -23,6 +25,41 @@ namespace views { namespace { +// SolidRoundRectPainter ------------------------------------------------------- + +class SolidRoundRectPainter : public Painter { + public: + SolidRoundRectPainter(SkColor color, float radius); + ~SolidRoundRectPainter() override; + + // Painter: + gfx::Size GetMinimumSize() const override; + void Paint(gfx::Canvas* canvas, const gfx::Size& size) override; + + private: + const SkColor color_; + const float radius_; + + DISALLOW_COPY_AND_ASSIGN(SolidRoundRectPainter); +}; + +SolidRoundRectPainter::SolidRoundRectPainter(SkColor color, float radius) + : color_(color), radius_(radius) {} + +SolidRoundRectPainter::~SolidRoundRectPainter() {} + +gfx::Size SolidRoundRectPainter::GetMinimumSize() const { + return gfx::Size(); +} + +void SolidRoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { + gfx::RectF rect((gfx::SizeF(size))); + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(color_); + canvas->DrawRoundRect(rect, radius_, paint); +} + // DashedFocusPainter ---------------------------------------------------------- class DashedFocusPainter : public Painter { @@ -151,10 +188,9 @@ void GradientPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { else p[1].iset(0, size.height()); - skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear( + paint.setShader(SkGradientShader::MakeLinear( p, colors_.get(), pos_.get(), count_, SkShader::kClamp_TileMode)); paint.setStyle(SkPaint::kFill_Style); - paint.setShader(s.get()); canvas->sk_canvas()->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(size.width()), @@ -236,6 +272,11 @@ void Painter::PaintFocusPainter(View* view, } // static +Painter* Painter::CreateSolidRoundRectPainter(SkColor color, float radius) { + return new SolidRoundRectPainter(color, radius); +} + +// static Painter* Painter::CreateHorizontalGradient(SkColor c1, SkColor c2) { SkColor colors[2]; colors[0] = c1; diff --git a/chromium/ui/views/painter.h b/chromium/ui/views/painter.h index d75688eb2a2..76cc996f50c 100644 --- a/chromium/ui/views/painter.h +++ b/chromium/ui/views/painter.h @@ -46,6 +46,10 @@ class VIEWS_EXPORT Painter { gfx::Canvas* canvas, Painter* focus_painter); + // Creates a painter that draws a RoundRect with a solid color and given + // corner radius. + static Painter* CreateSolidRoundRectPainter(SkColor color, float radius); + // Creates a painter that draws a gradient between the two colors. static Painter* CreateHorizontalGradient(SkColor c1, SkColor c2); static Painter* CreateVerticalGradient(SkColor c1, SkColor c2); diff --git a/chromium/ui/views/resources/default_100_percent/common/menu_radio_empty.png b/chromium/ui/views/resources/default_100_percent/common/menu_radio_empty.png Binary files differdeleted file mode 100644 index d2dbe3f87f0..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/menu_radio_empty.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_100_percent/common/menu_radio_selected.png b/chromium/ui/views/resources/default_100_percent/common/menu_radio_selected.png Binary files differdeleted file mode 100644 index 3aa9622048a..00000000000 --- a/chromium/ui/views/resources/default_100_percent/common/menu_radio_selected.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/menu_radio_empty.png b/chromium/ui/views/resources/default_200_percent/common/menu_radio_empty.png Binary files differdeleted file mode 100644 index 9ea7868a903..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/menu_radio_empty.png +++ /dev/null diff --git a/chromium/ui/views/resources/default_200_percent/common/menu_radio_selected.png b/chromium/ui/views/resources/default_200_percent/common/menu_radio_selected.png Binary files differdeleted file mode 100644 index d41e6423a43..00000000000 --- a/chromium/ui/views/resources/default_200_percent/common/menu_radio_selected.png +++ /dev/null diff --git a/chromium/ui/views/resources/views_resources.grd b/chromium/ui/views/resources/views_resources.grd index 743cc764472..7d6eb05ee83 100644 --- a/chromium/ui/views/resources/views_resources.grd +++ b/chromium/ui/views/resources/views_resources.grd @@ -159,8 +159,6 @@ <if expr="chromeos"> <structure type="chrome_scaled_image" name="IDR_MENU_CHECK" file="cros/menu_check.png" /> </if> - <structure type="chrome_scaled_image" name="IDR_MENU_RADIO_EMPTY" file="common/menu_radio_empty.png" /> - <structure type="chrome_scaled_image" name="IDR_MENU_RADIO_SELECTED" file="common/menu_radio_selected.png" /> <structure type="chrome_scaled_image" name="IDR_SLIDER_ACTIVE_LEFT" file="slider_left_active.png" /> <structure type="chrome_scaled_image" name="IDR_SLIDER_ACTIVE_RIGHT" file="slider_right_active.png" /> <structure type="chrome_scaled_image" name="IDR_SLIDER_ACTIVE_CENTER" file="slider_center_active.png" /> diff --git a/chromium/ui/views/run_all_unittests.cc b/chromium/ui/views/run_all_unittests.cc index d90123190e7..922343bdc91 100644 --- a/chromium/ui/views/run_all_unittests.cc +++ b/chromium/ui/views/run_all_unittests.cc @@ -17,6 +17,8 @@ #include "ui/aura/env.h" #endif +namespace views { + class ViewTestSuite : public base::TestSuite { public: ViewTestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {} @@ -47,10 +49,12 @@ class ViewTestSuite : public base::TestSuite { DISALLOW_COPY_AND_ASSIGN(ViewTestSuite); }; -int main(int argc, char** argv) { +int RunAllUnittests(int argc, char** argv) { ViewTestSuite test_suite(argc, argv); return base::LaunchUnitTests( argc, argv, base::Bind(&ViewTestSuite::Run, base::Unretained(&test_suite))); } + +} // namespace views diff --git a/chromium/ui/views/run_all_unittests.h b/chromium/ui/views/run_all_unittests.h new file mode 100644 index 00000000000..43239f80772 --- /dev/null +++ b/chromium/ui/views/run_all_unittests.h @@ -0,0 +1,14 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_RUN_ALL_UNITTESTS_H_ +#define UI_VIEWS_RUN_ALL_UNITTESTS_H_ + +namespace views { + +int RunAllUnittests(int argc, char** argv); + +} // namespace + +#endif // UI_VIEWS_RUN_ALL_UNITTESTS_H_ diff --git a/chromium/ui/views/run_all_unittests_main.cc b/chromium/ui/views/run_all_unittests_main.cc new file mode 100644 index 00000000000..5b26c760050 --- /dev/null +++ b/chromium/ui/views/run_all_unittests_main.cc @@ -0,0 +1,9 @@ +// 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/views/run_all_unittests.h" + +int main(int argc, char** argv) { + views::RunAllUnittests(argc, argv); +} diff --git a/chromium/ui/views/shadow_border.cc b/chromium/ui/views/shadow_border.cc index fb5888b1e8e..1f19c019c12 100644 --- a/chromium/ui/views/shadow_border.cc +++ b/chromium/ui/views/shadow_border.cc @@ -38,8 +38,7 @@ void ShadowBorder::Paint(const views::View& view, gfx::Canvas* canvas) { SkPaint paint; std::vector<gfx::ShadowValue> shadows; shadows.push_back(shadow_value_); - skia::RefPtr<SkDrawLooper> looper = gfx::CreateShadowDrawLooper(shadows); - paint.setLooper(looper.get()); + paint.setLooper(gfx::CreateShadowDrawLooper(shadows)); paint.setColor(SK_ColorTRANSPARENT); paint.setStrokeJoin(SkPaint::kRound_Join); gfx::Rect bounds(view.size()); diff --git a/chromium/ui/views/style/mac/combobox_background_mac.cc b/chromium/ui/views/style/mac/combobox_background_mac.cc new file mode 100644 index 00000000000..31997b15b78 --- /dev/null +++ b/chromium/ui/views/style/mac/combobox_background_mac.cc @@ -0,0 +1,57 @@ +// 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/views/style/mac/combobox_background_mac.h" + +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "ui/gfx/canvas.h" +#include "ui/native_theme/native_theme_mac.h" +#include "ui/views/controls/combobox/combobox.h" +#include "ui/views/view.h" + +namespace views { + +ComboboxBackgroundMac::ComboboxBackgroundMac() {} + +ComboboxBackgroundMac::~ComboboxBackgroundMac() {} + +void ComboboxBackgroundMac::Paint(gfx::Canvas* canvas, View* view) const { + DCHECK_EQ(view->GetClassName(), Combobox::kViewClassName); + Combobox* combobox = static_cast<Combobox*>(view); + + gfx::RectF bounds(combobox->GetLocalBounds()); + // Inset the left side far enough to draw only the arrow button, and inset the + // other three sides by half a pixel so the edge of the background doesn't + // paint outside the border. + bounds.Inset(bounds.width() - combobox->GetArrowButtonWidth(), 0.5, 0.5, 0.5); + + ui::NativeTheme::State state = ui::NativeTheme::kNormal; + if (!combobox->enabled()) + state = ui::NativeTheme::kDisabled; + + SkPaint paint; + paint.setShader( + ui::NativeThemeMac::GetButtonBackgroundShader( + state, + bounds.height())); + paint.setStyle(SkPaint::kFill_Style); + paint.setAntiAlias(true); + + SkPoint no_curve = SkPoint::Make(0, 0); + SkPoint curve = SkPoint::Make( + ui::NativeThemeMac::kComboboxCornerRadius, + ui::NativeThemeMac::kComboboxCornerRadius); + SkVector curves[4] = { no_curve, curve, curve, no_curve }; + + SkRRect fill_rect; + fill_rect.setRectRadii(gfx::RectFToSkRect(bounds), curves); + + SkPath path; + path.addRRect(fill_rect); + + canvas->DrawPath(path, paint); +} + +} // namespace views diff --git a/chromium/ui/views/style/mac/combobox_background_mac.h b/chromium/ui/views/style/mac/combobox_background_mac.h new file mode 100644 index 00000000000..5be50858d75 --- /dev/null +++ b/chromium/ui/views/style/mac/combobox_background_mac.h @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_STYLE_MAC_COMBOBOX_BACKGROUND_MAC_H_ +#define UI_VIEWS_STYLE_MAC_COMBOBOX_BACKGROUND_MAC_H_ + +#include "base/macros.h" +#include "ui/views/background.h" + +namespace gfx { +class Canvas; +} + +namespace views { + +// This class implements the background of comboboxes. Comboboxes look like +// this: +// --------------------------- +// | Option Text... | Arrows | +// --------------------------- +// This class colors the background of the arrows section in accordance with our +// Mac look and feel. +class ComboboxBackgroundMac : public Background { + public: + ComboboxBackgroundMac(); + ~ComboboxBackgroundMac() override; + + // Background: + void Paint(gfx::Canvas* canvas, View* view) const override; + private: + DISALLOW_COPY_AND_ASSIGN(ComboboxBackgroundMac); +}; + +} // namespace views + +#endif // UI_VIEWS_STYLE_MAC_COMBOBOX_BACKGROUND_MAC_H_ diff --git a/chromium/ui/views/style/mac/dialog_button_border_mac.cc b/chromium/ui/views/style/mac/dialog_button_border_mac.cc index 24cedaec8cb..366fbc28e9c 100644 --- a/chromium/ui/views/style/mac/dialog_button_border_mac.cc +++ b/chromium/ui/views/style/mac/dialog_button_border_mac.cc @@ -38,8 +38,8 @@ const double kInnerShadowBlurRadius = 2.0; const int kPaddingX = 14; const int kPaddingY = 4; -skia::RefPtr<SkShader> GetButtonGradient(int height, - Button::ButtonState state) { +sk_sp<SkShader> CreateButtonGradient(int height, + Button::ButtonState state) { ColorByState start = {0xFFF0F0F0, 0xFFF4F4F4, 0xFFEBEBEB, 0xFFEDEDED}; ColorByState end = {0xFFE0E0E0, 0xFFE4E4E4, 0xFFDBDBDB, 0xFFDEDEDE}; @@ -50,11 +50,9 @@ skia::RefPtr<SkShader> GetButtonGradient(int height, SkColor gradient_colors[] = {start[state], start[state], end[state]}; SkScalar gradient_positions[] = {0.0, 0.38, 1.0}; - skia::RefPtr<SkShader> gradient_shader = - skia::AdoptRef(SkGradientShader::CreateLinear( + return SkGradientShader::MakeLinear( gradient_points, gradient_colors, gradient_positions, 3, - SkShader::kClamp_TileMode)); - return gradient_shader; + SkShader::kClamp_TileMode); } void DrawConstrainedButtonBackground(const SkRect& button_rect, @@ -74,13 +72,10 @@ void DrawConstrainedButtonBackground(const SkRect& button_rect, std::vector<gfx::ShadowValue> shadows( 1, gfx::ShadowValue(gfx::Vector2d(0, kShadowOffsetY), blur, shadow[button_state])); - skia::RefPtr<SkDrawLooper> looper = gfx::CreateShadowDrawLooper(shadows); - paint.setLooper(looper.get()); + paint.setLooper(gfx::CreateShadowDrawLooper(shadows)); // Background. - skia::RefPtr<SkShader> gradient_shader = - GetButtonGradient(rect.height(), button_state); - paint.setShader(gradient_shader.get()); + paint.setShader(CreateButtonGradient(rect.height(), button_state)); paint.setStyle(SkPaint::kFill_Style); paint.setFlags(SkPaint::kAntiAlias_Flag); canvas->drawRoundRect(rect, kCornerRadius, kCornerRadius, paint); @@ -110,8 +105,7 @@ void DrawRoundRectInnerShadow(const SkRect& rect, SkPaint paint; std::vector<gfx::ShadowValue> shadows( 1, gfx::ShadowValue(shadow_offset, kInnerShadowBlurRadius, shadow_color)); - skia::RefPtr<SkDrawLooper> looper = gfx::CreateShadowDrawLooper(shadows); - paint.setLooper(looper.get()); + paint.setLooper(gfx::CreateShadowDrawLooper(shadows)); paint.setStyle(SkPaint::kFill_Style); paint.setColor(SK_ColorBLACK); // Note: Entirely clipped. diff --git a/chromium/ui/views/style/platform_style.cc b/chromium/ui/views/style/platform_style.cc index 866a1ca72c3..6719c9ba46b 100644 --- a/chromium/ui/views/style/platform_style.cc +++ b/chromium/ui/views/style/platform_style.cc @@ -5,13 +5,36 @@ #include "ui/views/style/platform_style.h" #include "build/build_config.h" -#include "ui/base/resource/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/resources/grit/ui_resources.h" +#include "ui/views/background.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/label_button_border.h" +#include "ui/views/controls/focusable_border.h" +#include "ui/views/controls/scrollbar/native_scroll_bar.h" namespace views { #if !defined(OS_MACOSX) + +// static +gfx::ImageSkia PlatformStyle::CreateComboboxArrow(bool is_enabled, + Combobox::Style style) { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return *rb.GetImageSkiaNamed(IDR_MENU_DROPARROW); +} + +// static +scoped_ptr<FocusableBorder> PlatformStyle::CreateComboboxBorder() { + return make_scoped_ptr(new FocusableBorder()); +} + +// static +scoped_ptr<Background> PlatformStyle::CreateComboboxBackground() { + return nullptr; +} + // static scoped_ptr<LabelButtonBorder> PlatformStyle::CreateLabelButtonBorder( Button::ButtonStyle style) { @@ -25,6 +48,11 @@ scoped_ptr<LabelButtonBorder> PlatformStyle::CreateLabelButtonBorder( Button::STYLE_TEXTBUTTON)); return border; } + +// static +scoped_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) { + return make_scoped_ptr(new NativeScrollBar(is_horizontal)); +} #endif #if !defined(OS_LINUX) || defined(OS_CHROMEOS) diff --git a/chromium/ui/views/style/platform_style.h b/chromium/ui/views/style/platform_style.h index e56fe0e30ba..3103691642a 100644 --- a/chromium/ui/views/style/platform_style.h +++ b/chromium/ui/views/style/platform_style.h @@ -8,16 +8,32 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "ui/views/controls/button/button.h" +#include "ui/views/controls/combobox/combobox.h" namespace views { class Border; +class FocusableBorder; class LabelButton; class LabelButtonBorder; +class ScrollBar; // Cross-platform API for providing platform-specific styling for toolkit-views. class PlatformStyle { public: + // Creates an ImageSkia containing the image to use for the combobox arrow. + // The |is_enabled| argument is true if the control the arrow is for is + // enabled, and false if the control is disabled. The |style| argument is the + // style of the combobox the arrow is being drawn for. + static gfx::ImageSkia CreateComboboxArrow(bool is_enabled, + Combobox::Style style); + + // Creates the appropriate border for a focusable Combobox. + static scoped_ptr<FocusableBorder> CreateComboboxBorder(); + + // Creates the appropriate background for a Combobox. + static scoped_ptr<Background> CreateComboboxBackground(); + // Creates the default label button border for the given |style|. Used when a // custom default border is not provided for a particular LabelButton class. static scoped_ptr<LabelButtonBorder> CreateLabelButtonBorder( @@ -26,6 +42,9 @@ class PlatformStyle { // Applies the current system theme to the default border created by |button|. static scoped_ptr<Border> CreateThemedLabelButtonBorder(LabelButton* button); + // Creates the default scrollbar for the given orientation. + static scoped_ptr<ScrollBar> CreateScrollBar(bool is_horizontal); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformStyle); }; diff --git a/chromium/ui/views/style/platform_style_mac.cc b/chromium/ui/views/style/platform_style_mac.cc deleted file mode 100644 index f1038d87842..00000000000 --- a/chromium/ui/views/style/platform_style_mac.cc +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/style/platform_style.h" - -#include "ui/views/controls/button/label_button.h" -#include "ui/views/controls/button/label_button_border.h" -#include "ui/views/style/mac/dialog_button_border_mac.h" - -namespace views { - -// static -scoped_ptr<LabelButtonBorder> PlatformStyle::CreateLabelButtonBorder( - Button::ButtonStyle style) { - if (style == Button::STYLE_BUTTON) - return make_scoped_ptr(new DialogButtonBorderMac()); - - return make_scoped_ptr(new LabelButtonAssetBorder(style)); -} - -} // namespace views diff --git a/chromium/ui/views/style/platform_style_mac.mm b/chromium/ui/views/style/platform_style_mac.mm new file mode 100644 index 00000000000..cbe9be51ce1 --- /dev/null +++ b/chromium/ui/views/style/platform_style_mac.mm @@ -0,0 +1,59 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/style/platform_style.h" + +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/vector_icons.h" +#include "ui/resources/grit/ui_resources.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/button/label_button_border.h" +#include "ui/views/controls/focusable_rounded_border_mac.h" +#import "ui/views/controls/scrollbar/cocoa_scroll_bar.h" +#include "ui/views/style/mac/combobox_background_mac.h" +#include "ui/views/style/mac/dialog_button_border_mac.h" + +namespace views { + +// static +gfx::ImageSkia PlatformStyle::CreateComboboxArrow(bool is_enabled, + Combobox::Style style) { + // TODO(ellyjones): IDR_MENU_DROPARROW is a cross-platform image that doesn't + // look right on Mac. See https://crbug.com/384071. + if (style == Combobox::STYLE_ACTION) { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return *rb.GetImageSkiaNamed(IDR_MENU_DROPARROW); + } + const int kComboboxArrowWidth = 13; + return gfx::CreateVectorIcon(gfx::VectorIconId::COMBOBOX_ARROW_MAC, + kComboboxArrowWidth, + is_enabled ? SK_ColorWHITE : SK_ColorBLACK); +} + +// static +scoped_ptr<FocusableBorder> PlatformStyle::CreateComboboxBorder() { + return make_scoped_ptr(new FocusableRoundedBorder); +} + +// static +scoped_ptr<Background> PlatformStyle::CreateComboboxBackground() { + return make_scoped_ptr(new ComboboxBackgroundMac); +} + +// static +scoped_ptr<LabelButtonBorder> PlatformStyle::CreateLabelButtonBorder( + Button::ButtonStyle style) { + if (style == Button::STYLE_BUTTON) + return make_scoped_ptr(new DialogButtonBorderMac()); + + return make_scoped_ptr(new LabelButtonAssetBorder(style)); +} + +// static +scoped_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) { + return make_scoped_ptr(new CocoaScrollBar(is_horizontal)); +} + +} // namespace views diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc b/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc index 06ab5f4434b..3bdbbd5f6c5 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc @@ -553,13 +553,15 @@ TEST_F(TouchSelectionControllerImplTest, textfield_->OnGestureEvent(&tap); // Select some text such that one handle is hidden. - textfield_->SelectRange(gfx::Range(10, textfield_text.length())); + textfield_->SelectRange( + gfx::Range(10u, static_cast<uint32_t>(textfield_text.length()))); // Check that one selection handle is hidden. EXPECT_FALSE(IsSelectionHandle1Visible()); EXPECT_TRUE(IsSelectionHandle2Visible()); - EXPECT_EQ(gfx::Range(10, textfield_text.length()), - textfield_->GetSelectedRange()); + EXPECT_EQ( + gfx::Range(10u, static_cast<uint32_t>(textfield_text.length())), + textfield_->GetSelectedRange()); // Drag the visible handle around and make sure the selection end point of the // invisible handle does not change. diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index 62ab2b1ba7a..d733034153b 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -106,7 +106,6 @@ View::View() enabled_(true), notify_enter_exit_on_child_(false), registered_for_visible_bounds_notification_(false), - clip_insets_(0, 0, 0, 0), needs_layout_(true), snap_layer_to_pixel_boundary_(false), flip_canvas_on_paint_for_rtl_ui_(false), @@ -178,11 +177,25 @@ void View::AddChildViewAt(View* view, int index) { // Sets the prev/next focus views. InitFocusSiblings(view, index); - // Let's insert the view. view->parent_ = this; children_.insert(children_.begin() + index, view); - views::Widget* widget = GetWidget(); + // Ensure the layer tree matches the view tree before calling to any client + // code. This way if client code further modifies the view tree we are in a + // sane state. + const bool did_reparent_any_layers = view->UpdateParentLayers(); + Widget* widget = GetWidget(); + if (did_reparent_any_layers && widget) + widget->UpdateRootLayers(); + + ReorderLayers(); + + // Make sure the visibility of the child layers are correct. + // If any of the parent View is hidden, then the layers of the subtree + // rooted at |this| should be hidden. Otherwise, all the child layers should + // inherit the visibility of the owner View. + view->UpdateLayerVisibility(); + if (widget) { const ui::NativeTheme* new_theme = view->GetNativeTheme(); if (new_theme != old_theme) @@ -195,23 +208,18 @@ void View::AddChildViewAt(View* view, int index) { v->ViewHierarchyChangedImpl(false, details); view->PropagateAddNotifications(details); + UpdateTooltip(); + if (widget) { RegisterChildrenForVisibleBoundsNotification(view); + if (view->visible()) view->SchedulePaint(); } if (layout_manager_.get()) layout_manager_->ViewAdded(this, view); - - ReorderLayers(); - - // Make sure the visibility of the child layers are correct. - // If any of the parent View is hidden, then the layers of the subtree - // rooted at |this| should be hidden. Otherwise, all the child layers should - // inherit the visibility of the owner View. - UpdateLayerVisibility(); } void View::ReorderChildView(View* view, int index) { @@ -319,10 +327,6 @@ gfx::Rect View::GetLocalBounds() const { return gfx::Rect(size()); } -gfx::Rect View::GetLayerBoundsInPixel() const { - return layer()->GetTargetBounds(); -} - gfx::Insets View::GetInsets() const { return border_.get() ? border_->GetInsets() : gfx::Insets(); } @@ -409,8 +413,10 @@ void View::SetVisible(bool visible) { AdvanceFocusIfNecessary(); // Notify the parent. - if (parent_) + if (parent_) { parent_->ChildVisibilityChanged(this); + parent_->NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, false); + } // This notifies all sub-views recursively. PropagateVisibilityNotifications(this, visible_); @@ -710,7 +716,7 @@ void View::ConvertPointFromScreen(const View* dst, gfx::Point* p) { if (!widget) return; *p -= widget->GetClientAreaBoundsInScreen().OffsetFromOrigin(); - views::View::ConvertPointFromWidget(dst, p); + ConvertPointFromWidget(dst, p); } gfx::Rect View::ConvertRectToParent(const gfx::Rect& rect) const { @@ -798,16 +804,17 @@ void View::Paint(const ui::PaintContext& parent_context) { // std::optional once we can do so. ui::ClipRecorder clip_recorder(parent_context); if (paint_relative_to_parent) { - // Set the clip rect to the bounds of this View. 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. - gfx::Rect clip_rect_in_parent = bounds(); - clip_rect_in_parent.Inset(clip_insets_); - if (parent_) - clip_rect_in_parent.set_x( - parent_->GetMirroredXForRect(clip_rect_in_parent)); - clip_recorder.ClipRect(clip_rect_in_parent); + // 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()); + } else { + gfx::Path clip_path_in_parent = clip_path_; + clip_path_in_parent.offset(GetMirroredX(), y()); + clip_recorder.ClipPathWithAntiAliasing(clip_path_in_parent); + } } ui::TransformRecorder transform_recorder(context); @@ -946,8 +953,7 @@ bool View::IsMouseHovered() const { if (!GetWidget()->IsMouseEventsEnabled()) return false; - gfx::Point cursor_pos(gfx::Screen::GetScreenFor( - GetWidget()->GetNativeView())->GetCursorScreenPoint()); + gfx::Point cursor_pos(gfx::Screen::GetScreen()->GetCursorScreenPoint()); ConvertPointFromScreen(this, &cursor_pos); return HitTestPoint(cursor_pos); } @@ -1025,7 +1031,7 @@ void View::OnMouseEvent(ui::MouseEvent* event) { return; case ui::ET_MOUSEWHEEL: - if (OnMouseWheel(*static_cast<ui::MouseWheelEvent*>(event))) + if (OnMouseWheel(*event->AsMouseWheelEvent())) event->SetHandled(); break; @@ -1436,12 +1442,6 @@ void View::OnPaintBorder(gfx::Canvas* canvas) { // Accelerated Painting -------------------------------------------------------- -void View::SetFillsBoundsOpaquely(bool fills_bounds_opaquely) { - // This method should not have the side-effect of creating the layer. - if (layer()) - layer()->SetFillsBoundsOpaquely(fills_bounds_opaquely); -} - gfx::Vector2d View::CalculateOffsetToAncestorWithLayer( ui::Layer** layer_parent) { if (layer()) { @@ -1805,39 +1805,47 @@ void View::DoRemoveChildView(View* view, DCHECK(view); const Views::iterator i(std::find(children_.begin(), children_.end(), view)); + if (i == children_.end()) + return; + scoped_ptr<View> view_to_be_deleted; - if (i != children_.end()) { - if (update_focus_cycle) { - // Let's remove the view from the focus traversal. - View* next_focusable = view->next_focusable_view_; - View* prev_focusable = view->previous_focusable_view_; - if (prev_focusable) - prev_focusable->next_focusable_view_ = next_focusable; - if (next_focusable) - next_focusable->previous_focusable_view_ = prev_focusable; - } + if (update_focus_cycle) { + View* next_focusable = view->next_focusable_view_; + View* prev_focusable = view->previous_focusable_view_; + if (prev_focusable) + prev_focusable->next_focusable_view_ = next_focusable; + if (next_focusable) + next_focusable->previous_focusable_view_ = prev_focusable; + } - if (GetWidget()) { - UnregisterChildrenForVisibleBoundsNotification(view); - if (view->visible()) - view->SchedulePaint(); - GetWidget()->NotifyWillRemoveView(view); - } + Widget* widget = GetWidget(); + if (widget) { + UnregisterChildrenForVisibleBoundsNotification(view); + if (view->visible()) + view->SchedulePaint(); - view->PropagateRemoveNotifications(this, new_parent); - view->parent_ = NULL; - view->UpdateLayerVisibility(); + if (!new_parent || new_parent->GetWidget() != widget) + widget->NotifyWillRemoveView(view); + } - if (delete_removed_view && !view->owned_by_client_) - view_to_be_deleted.reset(view); + // Make sure the layers belonging to the subtree rooted at |view| get + // removed. + view->OrphanLayers(); + if (widget) + widget->UpdateRootLayers(); - children_.erase(i); - } + view->PropagateRemoveNotifications(this, new_parent); + view->parent_ = nullptr; + + if (delete_removed_view && !view->owned_by_client_) + view_to_be_deleted.reset(view); + + children_.erase(i); if (update_tool_tip) UpdateTooltip(); - if (layout_manager_.get()) + if (layout_manager_) layout_manager_->ViewRemoved(this, view); } @@ -1878,20 +1886,6 @@ void View::ViewHierarchyChangedImpl( } } - if (details.is_add && layer() && !layer()->parent()) { - UpdateParentLayer(); - Widget* widget = GetWidget(); - if (widget) - widget->UpdateRootLayers(); - } else if (!details.is_add && details.child == this) { - // Make sure the layers belonging to the subtree rooted at |child| get - // removed from layers that do not belong in the same subtree. - OrphanLayers(); - Widget* widget = GetWidget(); - if (widget) - widget->UpdateRootLayers(); - } - ViewHierarchyChanged(details); details.parent->needs_layout_ = true; } @@ -2116,14 +2110,23 @@ void View::CreateLayer() { SchedulePaintOnParent(); } -void View::UpdateParentLayers() { +bool View::UpdateParentLayers() { // Attach all top-level un-parented layers. - if (layer() && !layer()->parent()) { - UpdateParentLayer(); - } else { - for (int i = 0, count = child_count(); i < count; ++i) - child_at(i)->UpdateParentLayers(); + if (layer()) { + if (!layer()->parent()) { + UpdateParentLayer(); + return true; + } + // The layers of any child views are already in place, so we can stop + // iterating here. + return false; } + bool result = false; + for (int i = 0, count = child_count(); i < count; ++i) { + if (child_at(i)->UpdateParentLayers()) + result = true; + } + return result; } void View::OrphanLayers() { diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h index a5ef00dae1d..a85ef3dfa00 100644 --- a/chromium/ui/views/view.h +++ b/chromium/ui/views/view.h @@ -31,9 +31,11 @@ #include "ui/events/event.h" #include "ui/events/event_target.h" #include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/path.h" #include "ui/views/view_targeter.h" #include "ui/views/views_export.h" @@ -232,9 +234,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // 0, 0). gfx::Rect GetLocalBounds() const; - // Returns the bounds of the layer in its own pixel coordinates. - gfx::Rect GetLayerBoundsInPixel() const; - // Returns the insets of the current border. If there is no border an empty // insets is returned. virtual gfx::Insets GetInsets() const; @@ -294,21 +293,14 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Returns whether the view is enabled. bool enabled() const { return enabled_; } - // This indicates that the view completely fills its bounds in an opaque - // color. This doesn't affect compositing but is a hint to the compositor to - // optimize painting. - // Note that this method does not implicitly create a layer if one does not - // already exist for the View, but is a no-op in that case. - void SetFillsBoundsOpaquely(bool fills_bounds_opaquely); - // Transformations ----------------------------------------------------------- // Methods for setting transformations for a view (e.g. rotation, scaling). gfx::Transform GetTransform() const; - // Clipping parameters. Clipping is done relative to the view bounds. - void set_clip_insets(gfx::Insets clip_insets) { clip_insets_ = clip_insets; } + // Clipping is done relative to the view's local bounds. + void set_clip_path(const gfx::Path& path) { clip_path_ = path; } // Sets the transform to the supplied transform. void SetTransform(const gfx::Transform& transform); @@ -497,7 +489,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // transformations are applied to it to convert it into the parent coordinate // system before propagating SchedulePaint up the view hierarchy. // TODO(beng): Make protected. - virtual void SchedulePaint(); + void SchedulePaint(); virtual void SchedulePaintInRect(const gfx::Rect& r); // Called by the framework to paint a View. Performs translation and clipping @@ -1347,9 +1339,11 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Creates the layer and related fields for this view. void CreateLayer(); - // Parents all un-parented layers within this view's hierarchy to this view's - // layer. - void UpdateParentLayers(); + // Recursively calls UpdateParentLayers() on all descendants, stopping at any + // Views that have layers. Calls UpdateParentLayer() for any Views that have + // a layer with no parent. If at least one descendant had an unparented layer + // true is returned. + bool UpdateParentLayers(); // Parents this view's layer to |parent_layer|, and sets its bounds and other // properties in accordance to |offset|, the view's offset from the @@ -1492,9 +1486,9 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Transformations ----------------------------------------------------------- - // Clipping parameters. skia transformation matrix does not give us clipping. - // So we do it ourselves. - gfx::Insets clip_insets_; + // Painting will be clipped to this path. TODO(estade): this doesn't work for + // layers. + gfx::Path clip_path_; // Layout -------------------------------------------------------------------- diff --git a/chromium/ui/views/view_targeter.cc b/chromium/ui/views/view_targeter.cc index 5e52ce9c2d6..641e28672b4 100644 --- a/chromium/ui/views/view_targeter.cc +++ b/chromium/ui/views/view_targeter.cc @@ -32,12 +32,10 @@ ui::EventTarget* ViewTargeter::FindTargetForEvent(ui::EventTarget* root, View* view = static_cast<View*>(root); if (event->IsKeyEvent()) - return FindTargetForKeyEvent(view, *static_cast<ui::KeyEvent*>(event)); + return FindTargetForKeyEvent(view, *event->AsKeyEvent()); - if (event->IsScrollEvent()) { - return FindTargetForScrollEvent(view, - *static_cast<ui::ScrollEvent*>(event)); - } + if (event->IsScrollEvent()) + return FindTargetForScrollEvent(view, *event->AsScrollEvent()); if (event->IsGestureEvent()) { ui::GestureEvent* gesture = event->AsGestureEvent(); diff --git a/chromium/ui/views/view_targeter_delegate.h b/chromium/ui/views/view_targeter_delegate.h index 0ad461abac4..44dd839a481 100644 --- a/chromium/ui/views/view_targeter_delegate.h +++ b/chromium/ui/views/view_targeter_delegate.h @@ -16,7 +16,8 @@ namespace views { class View; // Defines the default behaviour for hit-testing and event-targeting against a -// View using a rectangular region representing an event's location. Views +// View using a rectangular region representing an event's location (i.e., the +// bounding box of a gesture or a 1x1 rect in the case of a mouse event). Views // wishing to define custom hit-testing or event-targeting behaviour do so by // extending ViewTargeterDelegate and then installing a ViewTargeter on // themselves. @@ -25,9 +26,12 @@ class VIEWS_EXPORT ViewTargeterDelegate { ViewTargeterDelegate() {} virtual ~ViewTargeterDelegate() {} - // Returns true if the bounds of |target| intersects |rect|, where |rect| - // is in the local coodinate space of |target|. Overrides of this method by - // a View subclass should enforce DCHECK_EQ(this, target). + // Returns true if |target| should be considered as a candidate target for + // an event having |rect| as its location, where |rect| is in the local + // coordinate space of |target|. Overrides of this method by a View subclass + // should enforce DCHECK_EQ(this, target). + // TODO(tdanderson): Consider changing the name of this method to better + // reflect its purpose. virtual bool DoesIntersectRect(const View* target, const gfx::Rect& rect) const; diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc index a852c77d4ff..4b50b319ad2 100644 --- a/chromium/ui/views/view_unittest.cc +++ b/chromium/ui/views/view_unittest.cc @@ -31,6 +31,7 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/path.h" #include "ui/gfx/transform.h" +#include "ui/native_theme/native_theme.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/background.h" #include "ui/views/controls/native/native_view_host.h" @@ -2114,7 +2115,7 @@ TEST_F(ViewTest, HandleAccelerator) { // Create a window and add the view as its child. scoped_ptr<Widget> widget(new Widget); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(0, 0, 100, 100); widget->Init(params); View* root = widget->GetRootView(); @@ -2131,6 +2132,10 @@ TEST_F(ViewTest, HandleAccelerator) { EXPECT_EQ(0, view->accelerator_count_map_[return_accelerator]); #endif + // TYPE_POPUP widgets default to non-activatable, so the Show() above wouldn't + // have activated the Widget. First, allow activation. + widget->widget_delegate()->set_can_activate(true); + // When a non-child view is active, it should handle accelerators. view->accelerator_count_map_[return_accelerator] = 0; widget->Activate(); @@ -3505,12 +3510,8 @@ TEST_F(ViewTest, GetViewByID) { View::Views views; v1.GetViewsInGroup(kGroup, &views); EXPECT_EQ(2U, views.size()); - - View::Views::const_iterator i(std::find(views.begin(), views.end(), &v3)); - EXPECT_NE(views.end(), i); - - i = std::find(views.begin(), views.end(), &v4); - EXPECT_NE(views.end(), i); + EXPECT_NE(views.cend(), std::find(views.cbegin(), views.cend(), &v3)); + EXPECT_NE(views.cend(), std::find(views.cbegin(), views.cend(), &v4)); } TEST_F(ViewTest, AddExistingChild) { @@ -4477,4 +4478,96 @@ TEST_F(ViewTest, ScopedTargetHandlerReceivesEvents) { EXPECT_EQ(ui::ET_MOUSE_RELEASED, v->last_mouse_event_type_); } +// See comment above test for details. +class WidgetWithCustomTheme : public Widget { + public: + explicit WidgetWithCustomTheme(ui::NativeTheme* theme) : theme_(theme) {} + ~WidgetWithCustomTheme() override {} + + // Widget: + const ui::NativeTheme* GetNativeTheme() const override { return theme_; } + + private: + ui::NativeTheme* theme_; + + DISALLOW_COPY_AND_ASSIGN(WidgetWithCustomTheme); +}; + +// See comment above test for details. +class ViewThatAddsViewInOnNativeThemeChanged : public View { + public: + ViewThatAddsViewInOnNativeThemeChanged() { SetPaintToLayer(true); } + ~ViewThatAddsViewInOnNativeThemeChanged() override {} + + bool on_native_theme_changed_called() const { + return on_native_theme_changed_called_; + } + + // View: + void OnNativeThemeChanged(const ui::NativeTheme* theme) override { + on_native_theme_changed_called_ = true; + GetWidget()->GetRootView()->AddChildView(new View); + } + + private: + bool on_native_theme_changed_called_ = false; + + DISALLOW_COPY_AND_ASSIGN(ViewThatAddsViewInOnNativeThemeChanged); +}; + +// See comment above test for details. +class TestNativeTheme : public ui::NativeTheme { + public: + TestNativeTheme() {} + ~TestNativeTheme() override {} + + // ui::NativeTheme: + SkColor GetSystemColor(ColorId color_id) const override { + return SK_ColorRED; + } + gfx::Size GetPartSize(Part part, + State state, + const ExtraParams& extra) const override { + return gfx::Size(); + } + void Paint(SkCanvas* canvas, + Part part, + State state, + const gfx::Rect& rect, + const ExtraParams& extra) const override {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestNativeTheme); +}; + +// Creates and adds a new child view to |parent| that has a layer. +void AddViewWithChildLayer(View* parent) { + View* child = new View; + child->SetPaintToLayer(true); + parent->AddChildView(child); +} + +// This test does the following: +// . creates a couple of views with layers added to the root. +// . Add a view that overrides OnNativeThemeChanged(). In +// OnNativeThemeChanged() another view is added. +// This sequence triggered DCHECKs or crashes previously. This tests verifies +// that doesn't happen. Reason for crash was OnNativeThemeChanged() was called +// before the layer hierarchy was updated. OnNativeThemeChanged() should be +// called after the layer hierarchy matches the view hierarchy. +TEST_F(ViewTest, CrashOnAddFromFromOnNativeThemeChanged) { + TestNativeTheme theme; + WidgetWithCustomTheme widget(&theme); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 350, 350); + widget.Init(params); + + AddViewWithChildLayer(widget.GetRootView()); + ViewThatAddsViewInOnNativeThemeChanged* v = + new ViewThatAddsViewInOnNativeThemeChanged; + widget.GetRootView()->AddChildView(v); + EXPECT_TRUE(v->on_native_theme_changed_called()); +} + } // namespace views diff --git a/chromium/ui/views/views.gyp b/chromium/ui/views/views.gyp index 1530505225a..6f2243320b9 100644 --- a/chromium/ui/views/views.gyp +++ b/chromium/ui/views/views.gyp @@ -23,16 +23,26 @@ 'animation/ink_drop_animation_controller_factory.h', 'animation/ink_drop_animation_controller_impl.cc', 'animation/ink_drop_animation_controller_impl.h', - 'animation/ink_drop_animation_observer.cc', + 'animation/ink_drop_animation_ended_reason.cc', + 'animation/ink_drop_animation_ended_reason.h', 'animation/ink_drop_animation_observer.h', 'animation/ink_drop_delegate.h', 'animation/ink_drop_host.h', + 'animation/ink_drop_host_view.cc', + 'animation/ink_drop_host_view.h', + 'animation/ink_drop_hover.cc', + 'animation/ink_drop_hover.h', + 'animation/ink_drop_hover_observer.h', 'animation/ink_drop_painted_layer_delegates.cc', 'animation/ink_drop_painted_layer_delegates.h', 'animation/ink_drop_state.cc', 'animation/ink_drop_state.h', 'animation/scroll_animator.cc', 'animation/scroll_animator.h', + 'animation/flood_fill_ink_drop_animation.cc', + 'animation/flood_fill_ink_drop_animation.h', + 'animation/square_ink_drop_animation.cc', + 'animation/square_ink_drop_animation.h', 'background.cc', 'background.h', 'border.cc', @@ -41,6 +51,8 @@ 'bubble/bubble_border.h', 'bubble/bubble_delegate.cc', 'bubble/bubble_delegate.h', + 'bubble/bubble_dialog_delegate.cc', + 'bubble/bubble_dialog_delegate.h', 'bubble/bubble_frame_view.cc', 'bubble/bubble_frame_view.h', 'button_drag_utils.cc', @@ -59,6 +71,8 @@ 'cocoa/tooltip_manager_mac.mm', 'cocoa/views_nswindow_delegate.h', 'cocoa/views_nswindow_delegate.mm', + 'cocoa/views_scrollbar_bridge.h', + 'cocoa/views_scrollbar_bridge.mm', 'cocoa/widget_owner_nswindow_adapter.h', 'cocoa/widget_owner_nswindow_adapter.mm', 'color_chooser/color_chooser_listener.h', @@ -91,6 +105,8 @@ 'controls/combobox/combobox_listener.h', 'controls/focusable_border.cc', 'controls/focusable_border.h', + 'controls/focusable_rounded_border_mac.cc', + 'controls/focusable_rounded_border_mac.h', 'controls/glow_hover_controller.cc', 'controls/glow_hover_controller.h', 'controls/image_view.cc', @@ -126,8 +142,6 @@ 'controls/menu/menu_message_loop.h', 'controls/menu/menu_message_loop_mac.cc', 'controls/menu/menu_message_loop_mac.h', - 'controls/menu/menu_message_pump_dispatcher_win.cc', - 'controls/menu/menu_message_pump_dispatcher_win.h', 'controls/menu/menu_model_adapter.cc', 'controls/menu/menu_model_adapter.h', 'controls/menu/menu_runner.cc', @@ -172,6 +186,8 @@ 'controls/scrollbar/base_scroll_bar_button.h', 'controls/scrollbar/base_scroll_bar_thumb.cc', 'controls/scrollbar/base_scroll_bar_thumb.h', + 'controls/scrollbar/cocoa_scroll_bar.h', + 'controls/scrollbar/cocoa_scroll_bar.mm', 'controls/scrollbar/native_scroll_bar.cc', 'controls/scrollbar/native_scroll_bar.h', 'controls/scrollbar/native_scroll_bar_views.cc', @@ -276,11 +292,13 @@ 'round_rect_painter.h', 'shadow_border.cc', 'shadow_border.h', + 'style/mac/combobox_background_mac.cc', + 'style/mac/combobox_background_mac.h', 'style/mac/dialog_button_border_mac.cc', 'style/mac/dialog_button_border_mac.h', 'style/platform_style.cc', 'style/platform_style.h', - 'style/platform_style_mac.cc', + 'style/platform_style_mac.mm', 'view.cc', 'view.h', 'view_constants.cc', @@ -374,8 +392,6 @@ 'bubble/tray_bubble_view.cc', 'bubble/tray_bubble_view.h', 'controls/menu/display_change_listener_aura.cc', - 'controls/menu/menu_event_dispatcher.cc', - 'controls/menu/menu_event_dispatcher.h', 'controls/menu/menu_key_event_handler.cc', 'controls/menu/menu_key_event_handler.h', 'controls/menu/menu_message_loop_aura.cc', @@ -410,19 +426,10 @@ 'widget/window_reorderer.cc', 'widget/window_reorderer.h', ], - 'views_android_sources': [ - 'controls/menu/menu_config_android.cc', - 'widget/android/android_focus_rules.cc', - 'widget/android/android_focus_rules.h', - 'widget/android/native_widget_android.cc', - 'widget/android/native_widget_android.h', - ], 'views_desktop_aura_sources': [ 'widget/desktop_aura/desktop_capture_client.cc', 'widget/desktop_aura/desktop_capture_client.h', 'widget/desktop_aura/desktop_cursor_loader_updater.h', - 'widget/desktop_aura/desktop_dispatcher_client.cc', - 'widget/desktop_aura/desktop_dispatcher_client.h', 'widget/desktop_aura/desktop_drop_target_win.cc', 'widget/desktop_aura/desktop_drop_target_win.h', 'widget/desktop_aura/desktop_event_client.cc', @@ -483,10 +490,25 @@ 'widget/desktop_aura/desktop_window_tree_host_ozone.cc', ], 'views_test_support_sources': [ + 'animation/test/flood_fill_ink_drop_animation_test_api.cc', + 'animation/test/flood_fill_ink_drop_animation_test_api.h', 'animation/test/ink_drop_animation_test_api.cc', 'animation/test/ink_drop_animation_test_api.h', + 'animation/test/ink_drop_animation_controller_impl_test_api.cc', + 'animation/test/ink_drop_animation_controller_impl_test_api.h', + 'animation/test/ink_drop_hover_test_api.cc', + 'animation/test/ink_drop_hover_test_api.h', + 'animation/test/square_ink_drop_animation_test_api.cc', + 'animation/test/square_ink_drop_animation_test_api.h', + 'animation/test/test_ink_drop_animation_observer.cc', + 'animation/test/test_ink_drop_animation_observer.h', + 'animation/test/test_ink_drop_animation_observer_helper.h', 'animation/test/test_ink_drop_host.cc', 'animation/test/test_ink_drop_host.h', + 'animation/test/test_ink_drop_hover_observer.cc', + 'animation/test/test_ink_drop_hover_observer.h', + 'animation/test/test_ink_drop_delegate.cc', + 'animation/test/test_ink_drop_delegate.h', 'controls/textfield/textfield_test_api.cc', 'controls/textfield/textfield_test_api.h', 'test/capture_tracking_view.cc', @@ -501,6 +523,8 @@ 'test/focus_manager_test.h', 'test/menu_runner_test_api.cc', 'test/menu_runner_test_api.h', + 'test/native_widget_factory.cc', + 'test/native_widget_factory.h', 'test/scoped_views_test_helper.cc', 'test/scoped_views_test_helper.h', 'test/slider_test_api.cc', @@ -544,9 +568,13 @@ 'accessible_pane_view_unittest.cc', 'animation/bounds_animator_unittest.cc', 'animation/ink_drop_animation_controller_factory_unittest.cc', + 'animation/ink_drop_animation_controller_impl_unittest.cc', 'animation/ink_drop_animation_unittest.cc', + 'animation/ink_drop_hover_unittest.cc', + 'animation/square_ink_drop_animation_unittest.cc', 'bubble/bubble_border_unittest.cc', 'bubble/bubble_delegate_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', @@ -589,6 +617,8 @@ 'layout/grid_layout_unittest.cc', 'rect_based_targeting_utils_unittest.cc', 'run_all_unittests.cc', + 'run_all_unittests.h', + 'run_all_unittests_main.cc', 'style/mac/dialog_button_border_mac_unittest.cc', 'view_model_unittest.cc', 'view_model_utils_unittest.cc', @@ -607,6 +637,7 @@ 'widget/desktop_widget_unittest.cc', ], 'views_unittests_aura_sources': [ + 'accessibility/ax_aura_obj_cache_unittest.cc', 'controls/native/native_view_host_aura_unittest.cc', 'corewm/tooltip_controller_unittest.cc', 'touchui/touch_selection_controller_impl_unittest.cc', @@ -642,6 +673,7 @@ '../base/ime/ui_base_ime.gyp:ui_base_ime', '../base/ui_base.gyp:ui_base', '../compositor/compositor.gyp:compositor', + '../display/display.gyp:display', '../events/events.gyp:events', '../events/events.gyp:events_base', '../events/platform/events_platform.gyp:events_platform', @@ -669,7 +701,7 @@ '<@(views_sources)', ], 'conditions': [ - ['use_ash==0', { + ['use_aura==0', { 'sources!': [ 'bubble/tray_bubble_view.cc', 'bubble/tray_bubble_view.h', @@ -715,6 +747,7 @@ 'libraries': [ '-limm32.lib', '-loleacc.lib', + '-lwtsapi32.lib', ], 'msvs_settings': { 'VCLinkerTool': { @@ -795,6 +828,7 @@ '../../testing/gtest.gyp:gtest', '../base/ime/ui_base_ime.gyp:ui_base_ime', '../base/ui_base.gyp:ui_base', + '../base/ui_base.gyp:ui_base_test_support', '../compositor/compositor.gyp:compositor', '../compositor/compositor.gyp:compositor_test_support', '../events/events.gyp:events', @@ -810,10 +844,10 @@ ], 'sources': [ '<@(views_test_support_sources)', - # These two sources are not listed in views_test_support_sources as - # they are not used by the gn target that pulls in - # views_test_support_sources. + # These are not listed in views_test_support_sources as they are not + # used by the gn target that pulls in views_test_support_sources. 'test/default_platform_test_helper.cc', + 'test/native_widget_factory_desktop.cc', 'test/platform_test_helper.h', ], 'conditions': [ @@ -853,6 +887,7 @@ '../events/events.gyp:events_test_support', '../gfx/gfx.gyp:gfx', '../gfx/gfx.gyp:gfx_geometry', + '../native_theme/native_theme.gyp:native_theme', '../resources/ui_resources.gyp:ui_resources', '../resources/ui_resources.gyp:ui_test_pak', '../strings/ui_strings.gyp:ui_strings', @@ -889,17 +924,6 @@ }, }, }], - ['OS=="win" and win_use_allocator_shim==1', { - 'dependencies': [ - '../../base/allocator/allocator.gyp:allocator', - ], - }], - ['OS=="linux" and use_allocator!="none"', { - # See http://crbug.com/162998#c4 for why this is needed. - 'dependencies': [ - '../../base/allocator/allocator.gyp:allocator', - ], - }], ['use_x11==1', { 'dependencies': [ '../../build/linux/system.gyp:x11', @@ -948,6 +972,32 @@ }, # target_name: views_unittests ], # targets 'conditions': [ + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'views_unittests_run', + 'type': 'none', + 'dependencies': [ + 'views_unittests', + ], + 'includes': [ + '../../build/isolate.gypi', + ], + 'sources': [ + 'views_unittests.isolate', + ], + 'conditions': [ + ['use_x11==1', + { + 'dependencies': [ + '../../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck', + ], + } + ], + ], + }, + ], + }], ['OS=="mac"', { 'targets': [ { @@ -970,6 +1020,8 @@ 'sources': [ 'cocoa/bridged_native_widget_interactive_uitest.mm', 'run_all_unittests.cc', + 'run_all_unittests.h', + 'run_all_unittests_main.cc', 'widget/native_widget_mac_interactive_uitest.mm', ], 'conditions': [ diff --git a/chromium/ui/views/views_unittests.isolate b/chromium/ui/views/views_unittests.isolate new file mode 100644 index 00000000000..d58415f3942 --- /dev/null +++ b/chromium/ui/views/views_unittests.isolate @@ -0,0 +1,70 @@ +# Copyright (c) 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'conditions': [ + ['use_x11==0', { + 'variables': { + 'command': [ + '../../testing/test_env.py', + '<(PRODUCT_DIR)/views_unittests<(EXECUTABLE_SUFFIX)', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + '--asan=<(asan)', + '--msan=<(msan)', + '--tsan=<(tsan)', + ], + }, + }], + ['use_x11==1', { + 'variables': { + 'command': [ + '../../testing/xvfb.py', + '<(PRODUCT_DIR)', + '<(PRODUCT_DIR)/views_unittests<(EXECUTABLE_SUFFIX)', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + '--asan=<(asan)', + '--msan=<(msan)', + '--tsan=<(tsan)', + ], + 'files': [ + '../../testing/xvfb.py', + '<(PRODUCT_DIR)/xdisplaycheck<(EXECUTABLE_SUFFIX)', + ], + }, + }], + ['OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'files': [ + '../../testing/test_env.py', + '<(PRODUCT_DIR)/ui_test.pak', + ], + }, + }], + ['OS=="linux"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/libosmesa.so', + ], + }, + }], + ['OS=="mac"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/osmesa.so', + ], + }, + }], + ['OS=="win"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/osmesa.dll', + ], + }, + }], + ], + 'includes': [ + '../../base/base.isolate', + ], +} diff --git a/chromium/ui/views/widget/android/android_focus_rules.cc b/chromium/ui/views/widget/android/android_focus_rules.cc deleted file mode 100644 index c01ace9ca58..00000000000 --- a/chromium/ui/views/widget/android/android_focus_rules.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/widget/android/android_focus_rules.h" - -#include "ui/aura/window.h" - -namespace views { - -AndroidFocusRules::AndroidFocusRules() {} - -AndroidFocusRules::~AndroidFocusRules() {} - -bool AndroidFocusRules::SupportsChildActivation(aura::Window* window) const { - return window->IsRootWindow(); -} - -} // namespace views diff --git a/chromium/ui/views/widget/android/android_focus_rules.h b/chromium/ui/views/widget/android/android_focus_rules.h deleted file mode 100644 index 867d53d4812..00000000000 --- a/chromium/ui/views/widget/android/android_focus_rules.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_WIDGET_ANDROID_ANDROID_FOCUS_RULES_H_ -#define UI_VIEWS_WIDGET_ANDROID_ANDROID_FOCUS_RULES_H_ - -#include "base/macros.h" -#include "ui/wm/core/base_focus_rules.h" - -namespace views { - -// A set of focus rules for Android using aura. -class AndroidFocusRules : public wm::BaseFocusRules { - public: - AndroidFocusRules(); - ~AndroidFocusRules() override; - - private: - // wm::BaseFocusRules: - bool SupportsChildActivation(aura::Window* window) const override; - - DISALLOW_COPY_AND_ASSIGN(AndroidFocusRules); -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_ANDROID_ANDROID_FOCUS_RULES_H_ diff --git a/chromium/ui/views/widget/android/native_widget_android.cc b/chromium/ui/views/widget/android/native_widget_android.cc deleted file mode 100644 index 2aed158559a..00000000000 --- a/chromium/ui/views/widget/android/native_widget_android.cc +++ /dev/null @@ -1,841 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/widget/android/native_widget_android.h" - -#include "base/bind.h" -#include "base/macros.h" -#include "base/run_loop.h" -#include "base/strings/string_util.h" -#include "third_party/skia/include/core/SkRegion.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/client/cursor_client.h" -#include "ui/aura/client/default_capture_client.h" -#include "ui/aura/client/focus_client.h" -#include "ui/aura/client/screen_position_client.h" -#include "ui/aura/client/window_tree_client.h" -#include "ui/aura/env.h" -#include "ui/aura/window.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/aura/window_observer.h" -#include "ui/aura/window_tree_host.h" -#include "ui/base/dragdrop/os_exchange_data.h" -#include "ui/base/ui_base_types.h" -#include "ui/compositor/layer.h" -#include "ui/events/event.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/font_list.h" -#include "ui/gfx/screen.h" -#include "ui/native_theme/native_theme_aura.h" -#include "ui/views/drag_utils.h" -#include "ui/views/views_delegate.h" -#include "ui/views/widget/android/android_focus_rules.h" -#include "ui/views/widget/native_widget_aura.h" -#include "ui/views/widget/native_widget_delegate.h" -#include "ui/views/widget/root_view.h" -#include "ui/views/widget/tooltip_manager_aura.h" -#include "ui/views/widget/widget_aura_utils.h" -#include "ui/views/widget/widget_delegate.h" -#include "ui/views/widget/window_reorderer.h" -#include "ui/wm/core/default_activation_client.h" -#include "ui/wm/core/default_screen_position_client.h" -#include "ui/wm/core/focus_controller.h" -#include "ui/wm/core/shadow_types.h" -#include "ui/wm/core/window_animations.h" -#include "ui/wm/core/window_util.h" -#include "ui/wm/public/activation_client.h" -#include "ui/wm/public/dispatcher_client.h" -#include "ui/wm/public/drag_drop_client.h" -#include "ui/wm/public/window_move_client.h" -#include "ui/wm/public/window_types.h" - -// TODO(bshe): Most of the code is copied from NativeWidgetAura or -// DesktopNativeWidgetAura. Share more code instead of duplicate code when -// possible. crbug.com/554961. -namespace { - -class WindowTreeClientImpl : public aura::client::WindowTreeClient { - public: - explicit WindowTreeClientImpl(aura::Window* root_window) - : root_window_(root_window) { - aura::client::SetWindowTreeClient(root_window_, this); - } - ~WindowTreeClientImpl() override { - aura::client::SetWindowTreeClient(root_window_, nullptr); - } - - // Overridden from client::WindowTreeClient: - aura::Window* GetDefaultParent(aura::Window* context, - aura::Window* window, - const gfx::Rect& bounds) override { - return root_window_; - } - - private: - aura::Window* root_window_; - - DISALLOW_COPY_AND_ASSIGN(WindowTreeClientImpl); -}; - -// TODO(bshe|jonross): Get rid of nested message loop once crbug.com/523680 is -// fixed. -class AndroidDispatcherClient : public aura::client::DispatcherClient { - public: - AndroidDispatcherClient() {} - ~AndroidDispatcherClient() override {} - - // aura::client::DispatcherClient: - void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher, - base::Closure* run_closure, - base::Closure* quit_closure) override { - scoped_ptr<base::RunLoop> run_loop(new base::RunLoop()); - *quit_closure = run_loop->QuitClosure(); - *run_closure = - base::Bind(&AndroidDispatcherClient::RunNestedDispatcher, - base::Unretained(this), base::Passed(&run_loop), dispatcher); - } - - private: - void RunNestedDispatcher(scoped_ptr<base::RunLoop> run_loop, - base::MessagePumpDispatcher* dispatcher) { - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoop::ScopedNestableTaskAllower allow(loop); - run_loop->Run(); - } - - DISALLOW_COPY_AND_ASSIGN(AndroidDispatcherClient); -}; - -} // namespace - -namespace views { - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, public - -NativeWidgetAndroid::NativeWidgetAndroid( - internal::NativeWidgetDelegate* delegate) - : delegate_(delegate), - window_(new aura::Window(this)), - ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), - destroying_(false), - cursor_(gfx::kNullCursor), - saved_window_state_(ui::SHOW_STATE_DEFAULT), - close_widget_factory_(this) { - aura::client::SetFocusChangeObserver(window_, this); - aura::client::SetActivationChangeObserver(window_, this); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, internal::NativeWidgetPrivate implementation: - -void NativeWidgetAndroid::InitNativeWidget(const Widget::InitParams& params) { - ownership_ = params.ownership; - NativeWidgetAura::RegisterNativeWidgetForWindow(this, window_); - - window_->SetType(GetAuraWindowTypeForWidgetType(params.type)); - window_->Init(params.layer_type); - wm::SetShadowType(window_, wm::SHADOW_TYPE_NONE); - window_->Show(); - - // TODO(bshe): Get rid of the hard coded size. Tracked in crbug.com/551923. - host_.reset(aura::WindowTreeHost::Create(gfx::Rect(0, 0, 800, 600))); - host_->InitHost(); - host_->AddObserver(this); - - window_tree_client_.reset(new WindowTreeClientImpl(host_->window())); - - focus_client_.reset(new wm::FocusController(new AndroidFocusRules)); - aura::client::SetFocusClient(host_->window(), focus_client_.get()); - host_->window()->AddPreTargetHandler(focus_client_.get()); - - new wm::DefaultActivationClient(host_->window()); - - capture_client_.reset( - new aura::client::DefaultCaptureClient(host_->window())); - - screen_position_client_.reset(new wm::DefaultScreenPositionClient); - aura::client::SetScreenPositionClient(host_->window(), - screen_position_client_.get()); - dispatcher_client_.reset(new AndroidDispatcherClient); - aura::client::SetDispatcherClient(host_->window(), dispatcher_client_.get()); - - delegate_->OnNativeWidgetCreated(false); - - DCHECK(GetWidget()->GetRootView()); - if (params.type != Widget::InitParams::TYPE_TOOLTIP) - tooltip_manager_.reset(new views::TooltipManagerAura(GetWidget())); - - if (params.type != Widget::InitParams::TYPE_TOOLTIP && - params.type != Widget::InitParams::TYPE_POPUP) { - aura::client::SetDragDropDelegate(window_, this); - } - - aura::client::SetActivationDelegate(window_, this); - - host_->window()->AddChild(window_); - window_reorderer_.reset( - new WindowReorderer(window_, GetWidget()->GetRootView())); - - // TODO(bshe): figure out how to add cursor manager, drag drop client and all - // the necessary parts that exists in desktop_native_widget_aura. -} - -void NativeWidgetAndroid::OnWidgetInitDone() {} - -NonClientFrameView* NativeWidgetAndroid::CreateNonClientFrameView() { - NOTIMPLEMENTED(); - return nullptr; -} - -bool NativeWidgetAndroid::ShouldUseNativeFrame() const { - // There is only one frame type for aura. - return false; -} - -bool NativeWidgetAndroid::ShouldWindowContentsBeTransparent() const { - NOTIMPLEMENTED(); - return false; -} - -void NativeWidgetAndroid::FrameTypeChanged() { - NOTIMPLEMENTED(); -} - -Widget* NativeWidgetAndroid::GetWidget() { - return delegate_->AsWidget(); -} - -const Widget* NativeWidgetAndroid::GetWidget() const { - return delegate_->AsWidget(); -} - -gfx::NativeView NativeWidgetAndroid::GetNativeView() const { - return window_; -} - -gfx::NativeWindow NativeWidgetAndroid::GetNativeWindow() const { - return window_; -} - -Widget* NativeWidgetAndroid::GetTopLevelWidget() { - return GetWidget(); -} - -const ui::Compositor* NativeWidgetAndroid::GetCompositor() const { - return host_->compositor(); -} - -const ui::Layer* NativeWidgetAndroid::GetLayer() const { - return GetNativeWindow()->layer(); -} - -void NativeWidgetAndroid::ReorderNativeViews() { - window_reorderer_->ReorderChildWindows(); -} - -void NativeWidgetAndroid::ViewRemoved(View* view) { - // TODO(bshe): Implement drag and drop. crbug.com/554029. - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::SetNativeWindowProperty(const char* name, - void* value) { - GetNativeWindow()->SetNativeWindowProperty(name, value); -} - -void* NativeWidgetAndroid::GetNativeWindowProperty(const char* name) const { - return GetNativeWindow()->GetNativeWindowProperty(name); -} - -TooltipManager* NativeWidgetAndroid::GetTooltipManager() const { - return tooltip_manager_.get(); -} - -void NativeWidgetAndroid::SetCapture() { - GetNativeWindow()->SetCapture(); -} - -void NativeWidgetAndroid::ReleaseCapture() { - GetNativeWindow()->ReleaseCapture(); -} - -bool NativeWidgetAndroid::HasCapture() const { - return GetNativeWindow()->HasCapture(); -} - -ui::InputMethod* NativeWidgetAndroid::GetInputMethod() { - return host_->GetInputMethod(); -} - -void NativeWidgetAndroid::CenterWindow(const gfx::Size& size) { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::GetWindowPlacement( - gfx::Rect* bounds, - ui::WindowShowState* show_state) const { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -bool NativeWidgetAndroid::SetWindowTitle(const base::string16& title) { - if (GetNativeWindow()->title() == title) - return false; - GetNativeWindow()->SetTitle(title); - return true; -} - -void NativeWidgetAndroid::SetWindowIcons(const gfx::ImageSkia& window_icon, - const gfx::ImageSkia& app_icon) { - // TODO(bshe): Implement this. See crbug.com/554953. - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::InitModalType(ui::ModalType modal_type) { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -gfx::Rect NativeWidgetAndroid::GetWindowBoundsInScreen() const { - return GetNativeWindow()->GetBoundsInScreen(); -} - -gfx::Rect NativeWidgetAndroid::GetClientAreaBoundsInScreen() const { - // View-to-screen coordinate system transformations depend on this returning - // the full window bounds, for example View::ConvertPointToScreen(). - return GetNativeWindow()->GetBoundsInScreen(); -} - -gfx::Rect NativeWidgetAndroid::GetRestoredBounds() const { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); - return gfx::Rect(); -} - -void NativeWidgetAndroid::SetBounds(const gfx::Rect& bounds) { - // TODO(bshe): This may not work. We may need to resize SurfaceView too. See - // crbug.com/554952. - host_->SetBounds(bounds); -} - -void NativeWidgetAndroid::SetSize(const gfx::Size& size) { - gfx::Rect bounds = host_->GetBounds(); - SetBounds(gfx::Rect(bounds.origin(), size)); -} - -void NativeWidgetAndroid::StackAbove(gfx::NativeView native_view) { - // TODO(bshe): Implements window stacking logic. See crbug.com/554047 - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::StackAtTop() { - // TODO(bshe): Implements window stacking logic. See crbug.com/554047 - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::StackBelow(gfx::NativeView native_view) { - // TODO(bshe): Implements window stacking logic. See crbug.com/554047 - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::SetShape(SkRegion* region) { - GetNativeWindow()->layer()->SetAlphaShape(make_scoped_ptr(region)); -} - -void NativeWidgetAndroid::Close() { - // TODO(bshe): This might not be right. See crbug.com/554259. - DCHECK(ownership_ == Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET); - GetNativeWindow()->SuppressPaint(); - Hide(); - GetNativeWindow()->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE); - - if (!close_widget_factory_.HasWeakPtrs()) { - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&NativeWidgetAndroid::CloseNow, - close_widget_factory_.GetWeakPtr())); - } -} - -void NativeWidgetAndroid::CloseNow() { - // TODO(bshe): This might not be right. See crbug.com/554259. - host_->RemoveObserver(this); - host_.reset(); - delete window_; -} - -void NativeWidgetAndroid::Show() { - host_->Show(); - GetNativeWindow()->Show(); -} - -void NativeWidgetAndroid::Hide() { - host_->Hide(); - GetNativeWindow()->Hide(); -} - -void NativeWidgetAndroid::ShowMaximizedWithBounds( - const gfx::Rect& restored_bounds) { - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::ShowWithWindowState(ui::WindowShowState state) { - NOTIMPLEMENTED(); -} - -bool NativeWidgetAndroid::IsVisible() const { - return GetNativeWindow()->IsVisible(); -} - -void NativeWidgetAndroid::Activate() { - aura::client::GetActivationClient(host_->window()) - ->ActivateWindow(GetNativeWindow()); -} - -void NativeWidgetAndroid::Deactivate() { - aura::client::GetActivationClient(host_->window()) - ->DeactivateWindow(GetNativeWindow()); -} - -bool NativeWidgetAndroid::IsActive() const { - return wm::IsActiveWindow(GetNativeWindow()); -} - -void NativeWidgetAndroid::SetAlwaysOnTop(bool on_top) { - GetNativeWindow()->SetProperty(aura::client::kAlwaysOnTopKey, on_top); -} - -bool NativeWidgetAndroid::IsAlwaysOnTop() const { - return GetNativeWindow()->GetProperty(aura::client::kAlwaysOnTopKey); -} - -void NativeWidgetAndroid::SetVisibleOnAllWorkspaces(bool always_visible) { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::Maximize() { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::Minimize() { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -bool NativeWidgetAndroid::IsMaximized() const { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); - return false; -} - -bool NativeWidgetAndroid::IsMinimized() const { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); - return false; -} - -void NativeWidgetAndroid::Restore() { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::SetFullscreen(bool fullscreen) { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -bool NativeWidgetAndroid::IsFullscreen() const { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); - return false; -} - -void NativeWidgetAndroid::SetOpacity(unsigned char opacity) { - GetNativeWindow()->layer()->SetOpacity(opacity / 255.0); -} - -void NativeWidgetAndroid::SetUseDragFrame(bool use_drag_frame) { - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::FlashFrame(bool flash) { - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::RunShellDrag( - View* view, - const ui::OSExchangeData& data, - const gfx::Point& location, - int operation, - ui::DragDropTypes::DragEventSource source) { - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::SchedulePaintInRect(const gfx::Rect& rect) { - GetNativeWindow()->SchedulePaintInRect(rect); -} - -void NativeWidgetAndroid::SetCursor(gfx::NativeCursor cursor) { - cursor_ = cursor; - aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(host_->window()); - if (cursor_client) - cursor_client->SetCursor(cursor); -} - -bool NativeWidgetAndroid::IsMouseEventsEnabled() const { - aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(host_->window()); - return cursor_client ? cursor_client->IsMouseEventsEnabled() : true; -} - -void NativeWidgetAndroid::ClearNativeFocus() { - aura::client::FocusClient* client = aura::client::GetFocusClient(window_); - if (client && window_->Contains(client->GetFocusedWindow())) - client->ResetFocusWithinActiveWindow(window_); -} - -gfx::Rect NativeWidgetAndroid::GetWorkAreaBoundsInScreen() const { - return gfx::Screen::GetScreenFor(window_) - ->GetDisplayNearestWindow(window_) - .work_area(); -} - -Widget::MoveLoopResult NativeWidgetAndroid::RunMoveLoop( - const gfx::Vector2d& drag_offset, - Widget::MoveLoopSource source, - Widget::MoveLoopEscapeBehavior escape_behavior) { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); - return Widget::MOVE_LOOP_SUCCESSFUL; -} - -void NativeWidgetAndroid::EndMoveLoop() { - // TODO(bshe): Implement this. See crbug.com/554208. - NOTIMPLEMENTED(); -} - -void NativeWidgetAndroid::SetVisibilityChangedAnimationsEnabled(bool value) { - GetNativeWindow()->SetProperty(aura::client::kAnimationsDisabledKey, !value); -} - -void NativeWidgetAndroid::SetVisibilityAnimationDuration( - const base::TimeDelta& duration) { - wm::SetWindowVisibilityAnimationDuration(GetNativeWindow(), duration); -} - -void NativeWidgetAndroid::SetVisibilityAnimationTransition( - Widget::VisibilityTransition transition) { - wm::WindowVisibilityAnimationTransition wm_transition = wm::ANIMATE_NONE; - switch (transition) { - case Widget::ANIMATE_SHOW: - wm_transition = wm::ANIMATE_SHOW; - break; - case Widget::ANIMATE_HIDE: - wm_transition = wm::ANIMATE_HIDE; - break; - case Widget::ANIMATE_BOTH: - wm_transition = wm::ANIMATE_BOTH; - break; - case Widget::ANIMATE_NONE: - wm_transition = wm::ANIMATE_NONE; - break; - } - wm::SetWindowVisibilityAnimationTransition(GetNativeWindow(), wm_transition); -} - -ui::NativeTheme* NativeWidgetAndroid::GetNativeTheme() const { - return ui::NativeThemeAura::instance(); -} - -void NativeWidgetAndroid::OnRootViewLayout() { - NOTIMPLEMENTED(); -} - -bool NativeWidgetAndroid::IsTranslucentWindowOpacitySupported() const { - return true; -} - -void NativeWidgetAndroid::OnSizeConstraintsChanged() { - window_->SetProperty(aura::client::kCanMaximizeKey, - GetWidget()->widget_delegate()->CanMaximize()); - window_->SetProperty(aura::client::kCanMinimizeKey, - GetWidget()->widget_delegate()->CanMinimize()); - window_->SetProperty(aura::client::kCanResizeKey, - GetWidget()->widget_delegate()->CanResize()); -} - -void NativeWidgetAndroid::RepostNativeEvent(gfx::NativeEvent native_event) { - OnEvent(native_event); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, aura::WindowDelegate implementation: - -gfx::Size NativeWidgetAndroid::GetMinimumSize() const { - return delegate_->GetMinimumSize(); -} - -gfx::Size NativeWidgetAndroid::GetMaximumSize() const { - // If a window have a maximum size, the window should not be - // maximizable. - DCHECK(delegate_->GetMaximumSize().IsEmpty() || - !window_->GetProperty(aura::client::kCanMaximizeKey)); - return delegate_->GetMaximumSize(); -} - -void NativeWidgetAndroid::OnBoundsChanged(const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) { - // Assume that if the old bounds was completely empty a move happened. This - // handles the case of a maximize animation acquiring the layer (acquiring a - // layer results in clearing the bounds). - if (old_bounds.origin() != new_bounds.origin() || - (old_bounds == gfx::Rect(0, 0, 0, 0) && !new_bounds.IsEmpty())) { - delegate_->OnNativeWidgetMove(); - } - if (old_bounds.size() != new_bounds.size()) - delegate_->OnNativeWidgetSizeChanged(new_bounds.size()); -} - -gfx::NativeCursor NativeWidgetAndroid::GetCursor(const gfx::Point& point) { - return cursor_; -} - -int NativeWidgetAndroid::GetNonClientComponent(const gfx::Point& point) const { - return delegate_->GetNonClientComponent(point); -} - -bool NativeWidgetAndroid::ShouldDescendIntoChildForEventHandling( - aura::Window* child, - const gfx::Point& location) { - views::WidgetDelegate* widget_delegate = GetWidget()->widget_delegate(); - if (widget_delegate && - !widget_delegate->ShouldDescendIntoChildForEventHandling(child, location)) - return false; - - // Don't descend into |child| if there is a view with a Layer that contains - // the point and is stacked above |child|s layer. - typedef std::vector<ui::Layer*> Layers; - const Layers& root_layers(delegate_->GetRootLayers()); - if (root_layers.empty()) - return true; - - Layers::const_iterator child_layer_iter( - std::find(window_->layer()->children().begin(), - window_->layer()->children().end(), child->layer())); - if (child_layer_iter == window_->layer()->children().end()) - return true; - - for (std::vector<ui::Layer*>::const_reverse_iterator i = root_layers.rbegin(); - i != root_layers.rend(); ++i) { - ui::Layer* layer = *i; - if (layer->visible() && layer->bounds().Contains(location)) { - Layers::const_iterator root_layer_iter( - std::find(window_->layer()->children().begin(), - window_->layer()->children().end(), layer)); - if (root_layer_iter > child_layer_iter) - return false; - } - } - return true; -} - -bool NativeWidgetAndroid::CanFocus() { - return ShouldActivate(); -} - -void NativeWidgetAndroid::OnCaptureLost() { - delegate_->OnMouseCaptureLost(); -} - -void NativeWidgetAndroid::OnPaint(const ui::PaintContext& context) { - delegate_->OnNativeWidgetPaint(context); -} - -void NativeWidgetAndroid::OnDeviceScaleFactorChanged( - float device_scale_factor) { - GetWidget()->DeviceScaleFactorChanged(device_scale_factor); -} - -void NativeWidgetAndroid::OnWindowDestroying(aura::Window* window) { - delegate_->OnNativeWidgetDestroying(); - - // If the aura::Window is destroyed, we can no longer show tooltips. - tooltip_manager_.reset(); -} - -void NativeWidgetAndroid::OnWindowDestroyed(aura::Window* window) { - window_ = nullptr; - delegate_->OnNativeWidgetDestroyed(); - if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) - delete this; -} - -void NativeWidgetAndroid::OnWindowTargetVisibilityChanged(bool visible) { - delegate_->OnNativeWidgetVisibilityChanged(visible); -} - -bool NativeWidgetAndroid::HasHitTestMask() const { - return delegate_->HasHitTestMask(); -} - -void NativeWidgetAndroid::GetHitTestMask(gfx::Path* mask) const { - DCHECK(mask); - delegate_->GetHitTestMask(mask); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, ui::EventHandler implementation: - -void NativeWidgetAndroid::OnKeyEvent(ui::KeyEvent* event) { - DCHECK(window_); - // Renderer may send a key event back to us if the key event wasn't handled, - // and the window may be invisible by that time. - if (!window_->IsVisible()) - return; - - FocusManager* focus_manager = GetWidget()->GetFocusManager(); - delegate_->OnKeyEvent(event); - if (!event->handled() && focus_manager) - focus_manager->OnKeyEvent(*event); - event->SetHandled(); -} - -void NativeWidgetAndroid::OnMouseEvent(ui::MouseEvent* event) { - DCHECK(window_); - DCHECK(window_->IsVisible()); - if (event->type() == ui::ET_MOUSEWHEEL) { - delegate_->OnMouseEvent(event); - if (event->handled()) - return; - } - - if (tooltip_manager_.get()) - tooltip_manager_->UpdateTooltip(); - TooltipManagerAura::UpdateTooltipManagerForCapture(GetWidget()); - delegate_->OnMouseEvent(event); -} - -void NativeWidgetAndroid::OnScrollEvent(ui::ScrollEvent* event) { - delegate_->OnScrollEvent(event); -} - -void NativeWidgetAndroid::OnGestureEvent(ui::GestureEvent* event) { - DCHECK(window_); - DCHECK(window_->IsVisible() || event->IsEndingEvent()); - delegate_->OnGestureEvent(event); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, aura::client::ActivationDelegate implementation: - -bool NativeWidgetAndroid::ShouldActivate() const { - return delegate_->CanActivate(); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, aura::client::ActivationChangeObserver -// implementation: - -void NativeWidgetAndroid::OnWindowActivated( - aura::client::ActivationChangeObserver::ActivationReason, - aura::Window* gained_active, - aura::Window* lost_active) { - DCHECK(window_ == gained_active || window_ == lost_active); - if (GetWidget()->GetFocusManager()) { - if (window_ == gained_active) - GetWidget()->GetFocusManager()->RestoreFocusedView(); - else if (window_ == lost_active) - GetWidget()->GetFocusManager()->StoreFocusedView(true); - } - delegate_->OnNativeWidgetActivationChanged(window_ == gained_active); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, aura::client::FocusChangeObserver: - -void NativeWidgetAndroid::OnWindowFocused(aura::Window* gained_focus, - aura::Window* lost_focus) { - if (window_ == gained_focus) - delegate_->OnNativeFocus(); - else if (window_ == lost_focus) - delegate_->OnNativeBlur(); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, aura::WindowDragDropDelegate implementation: - -void NativeWidgetAndroid::OnDragEntered(const ui::DropTargetEvent& event) { - // TODO: Implement drag and drop. crbug.com/554029. - NOTIMPLEMENTED(); -} - -int NativeWidgetAndroid::OnDragUpdated(const ui::DropTargetEvent& event) { - // TODO: Implement drag and drop. crbug.com/554029. - NOTIMPLEMENTED(); - return 0; -} - -void NativeWidgetAndroid::OnDragExited() { - // TODO: Implement drag and drop. crbug.com/554029. - NOTIMPLEMENTED(); -} - -int NativeWidgetAndroid::OnPerformDrop(const ui::DropTargetEvent& event) { - // TODO: Implement drag and drop. crbug.com/554029. - NOTIMPLEMENTED(); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, aura::WindowTreeHostObserver implementation: - -void NativeWidgetAndroid::OnHostCloseRequested( - const aura::WindowTreeHost* host) { - GetWidget()->Close(); -} - -void NativeWidgetAndroid::OnHostResized(const aura::WindowTreeHost* host) { - gfx::Rect new_bounds = gfx::Rect(host_->window()->bounds().size()); - GetNativeWindow()->SetBounds(new_bounds); - delegate_->OnNativeWidgetSizeChanged(new_bounds.size()); -} - -void NativeWidgetAndroid::OnHostMoved(const aura::WindowTreeHost* host, - const gfx::Point& new_origin) { - TRACE_EVENT1("views", "NativeWidgetAndroid::OnHostMoved", "new_origin", - new_origin.ToString()); - - delegate_->OnNativeWidgetMove(); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, protected: - -NativeWidgetAndroid::~NativeWidgetAndroid() { - destroying_ = true; - if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) - delete delegate_; - else - CloseNow(); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAndroid, private: - -bool NativeWidgetAndroid::IsDocked() const { - NOTIMPLEMENTED(); - return false; -} - -void NativeWidgetAndroid::SetInitialFocus(ui::WindowShowState show_state) { - // The window does not get keyboard messages unless we focus it. - if (!GetWidget()->SetInitialFocus(show_state)) - window_->Focus(); -} - -} // namespace views diff --git a/chromium/ui/views/widget/android/native_widget_android.h b/chromium/ui/views/widget/android/native_widget_android.h deleted file mode 100644 index 8aaa3c470bd..00000000000 --- a/chromium/ui/views/widget/android/native_widget_android.h +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_WIDGET_ANDROID_NATIVE_WIDGET_ANDROID_H_ -#define UI_VIEWS_WIDGET_ANDROID_NATIVE_WIDGET_ANDROID_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "ui/aura/client/focus_change_observer.h" -#include "ui/aura/window_delegate.h" -#include "ui/aura/window_observer.h" -#include "ui/aura/window_tree_host_observer.h" -#include "ui/base/cursor/cursor.h" -#include "ui/events/event_constants.h" -#include "ui/views/views_export.h" -#include "ui/views/widget/native_widget_private.h" -#include "ui/wm/public/activation_change_observer.h" -#include "ui/wm/public/activation_delegate.h" -#include "ui/wm/public/drag_drop_delegate.h" - -namespace aura { -class Window; -class WindowTreeHost; -namespace client { -class DefaultCaptureClient; -class DispatcherClient; -class ScreenPositionClient; -class WindowTreeClient; -} -} -namespace gfx { -class FontList; -} -namespace wm { -class FocusController; -} - -namespace views { - -class TooltipManagerAura; -class WindowReorderer; - -// NativeWidgetAndroid creates and hosts the Widget in an Android native window. -// It is used to create a top level window on Android platform. -class VIEWS_EXPORT NativeWidgetAndroid - : public internal::NativeWidgetPrivate, - public aura::WindowDelegate, - public aura::client::ActivationDelegate, - public aura::client::ActivationChangeObserver, - public aura::client::FocusChangeObserver, - public aura::client::DragDropDelegate, - public aura::WindowTreeHostObserver { - public: - explicit NativeWidgetAndroid(internal::NativeWidgetDelegate* delegate); - - aura::WindowTreeHost* host() { return host_.get(); } - - // Overridden from internal::NativeWidgetPrivate: - void InitNativeWidget(const Widget::InitParams& params) override; - void OnWidgetInitDone() override; - NonClientFrameView* CreateNonClientFrameView() override; - bool ShouldUseNativeFrame() const override; - bool ShouldWindowContentsBeTransparent() const override; - void FrameTypeChanged() override; - Widget* GetWidget() override; - const Widget* GetWidget() const override; - gfx::NativeView GetNativeView() const override; - gfx::NativeWindow GetNativeWindow() const override; - Widget* GetTopLevelWidget() override; - const ui::Compositor* GetCompositor() const override; - const ui::Layer* GetLayer() const override; - void ReorderNativeViews() override; - void ViewRemoved(View* view) override; - void SetNativeWindowProperty(const char* name, void* value) override; - void* GetNativeWindowProperty(const char* name) const override; - TooltipManager* GetTooltipManager() const override; - void SetCapture() override; - void ReleaseCapture() override; - bool HasCapture() const override; - ui::InputMethod* GetInputMethod() override; - void CenterWindow(const gfx::Size& size) override; - void GetWindowPlacement(gfx::Rect* bounds, - ui::WindowShowState* maximized) const override; - bool SetWindowTitle(const base::string16& title) override; - void SetWindowIcons(const gfx::ImageSkia& window_icon, - const gfx::ImageSkia& app_icon) override; - void InitModalType(ui::ModalType modal_type) override; - gfx::Rect GetWindowBoundsInScreen() const override; - gfx::Rect GetClientAreaBoundsInScreen() const override; - gfx::Rect GetRestoredBounds() const override; - void SetBounds(const gfx::Rect& bounds) override; - void SetSize(const gfx::Size& size) override; - void StackAbove(gfx::NativeView native_view) override; - void StackAtTop() override; - void StackBelow(gfx::NativeView native_view) override; - void SetShape(SkRegion* shape) override; - void Close() override; - void CloseNow() override; - void Show() override; - void Hide() override; - void ShowMaximizedWithBounds(const gfx::Rect& restored_bounds) override; - void ShowWithWindowState(ui::WindowShowState state) override; - bool IsVisible() const override; - void Activate() override; - void Deactivate() override; - bool IsActive() const override; - void SetAlwaysOnTop(bool always_on_top) override; - bool IsAlwaysOnTop() const override; - void SetVisibleOnAllWorkspaces(bool always_visible) override; - void Maximize() override; - void Minimize() override; - bool IsMaximized() const override; - bool IsMinimized() const override; - void Restore() override; - void SetFullscreen(bool fullscreen) override; - bool IsFullscreen() const override; - void SetOpacity(unsigned char opacity) override; - void SetUseDragFrame(bool use_drag_frame) override; - void FlashFrame(bool flash_frame) override; - void RunShellDrag(View* view, - const ui::OSExchangeData& data, - const gfx::Point& location, - int operation, - ui::DragDropTypes::DragEventSource source) override; - void SchedulePaintInRect(const gfx::Rect& rect) override; - void SetCursor(gfx::NativeCursor cursor) override; - bool IsMouseEventsEnabled() const override; - void ClearNativeFocus() override; - gfx::Rect GetWorkAreaBoundsInScreen() const override; - Widget::MoveLoopResult RunMoveLoop( - const gfx::Vector2d& drag_offset, - Widget::MoveLoopSource source, - Widget::MoveLoopEscapeBehavior escape_behavior) override; - void EndMoveLoop() override; - void SetVisibilityChangedAnimationsEnabled(bool value) override; - void SetVisibilityAnimationDuration(const base::TimeDelta& duration) override; - void SetVisibilityAnimationTransition( - Widget::VisibilityTransition transition) override; - ui::NativeTheme* GetNativeTheme() const override; - void OnRootViewLayout() override; - bool IsTranslucentWindowOpacitySupported() const override; - void OnSizeConstraintsChanged() override; - void RepostNativeEvent(gfx::NativeEvent native_event) override; - - // Overridden from aura::WindowDelegate: - gfx::Size GetMinimumSize() const override; - gfx::Size GetMaximumSize() const override; - void OnBoundsChanged(const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) override; - gfx::NativeCursor GetCursor(const gfx::Point& point) override; - int GetNonClientComponent(const gfx::Point& point) const override; - bool ShouldDescendIntoChildForEventHandling( - aura::Window* child, - const gfx::Point& location) override; - bool CanFocus() override; - 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; - void OnWindowTargetVisibilityChanged(bool visible) override; - bool HasHitTestMask() const override; - void GetHitTestMask(gfx::Path* mask) const override; - - // Overridden from ui::EventHandler: - void OnKeyEvent(ui::KeyEvent* event) override; - void OnMouseEvent(ui::MouseEvent* event) override; - void OnScrollEvent(ui::ScrollEvent* event) override; - void OnGestureEvent(ui::GestureEvent* event) override; - - // Overridden from aura::client::ActivationDelegate: - bool ShouldActivate() const override; - - // Overridden from aura::client::ActivationChangeObserver: - void OnWindowActivated( - aura::client::ActivationChangeObserver::ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) override; - - // Overridden from aura::client::FocusChangeObserver: - void OnWindowFocused(aura::Window* gained_focus, - aura::Window* lost_focus) override; - - // Overridden from aura::client::DragDropDelegate: - void OnDragEntered(const ui::DropTargetEvent& event) override; - int OnDragUpdated(const ui::DropTargetEvent& event) override; - void OnDragExited() override; - 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 OnHostMoved(const aura::WindowTreeHost* host, - const gfx::Point& new_origin) override; - - protected: - ~NativeWidgetAndroid() override; - - internal::NativeWidgetDelegate* delegate() { return delegate_; } - - private: - class ActiveWindowObserver; - - bool IsDocked() const; - void SetInitialFocus(ui::WindowShowState show_state); - - internal::NativeWidgetDelegate* delegate_; - - // WARNING: set to NULL when destroyed. As the Widget is not necessarily - // destroyed along with |window_| all usage of |window_| should first verify - // non-NULL. - aura::Window* window_; - - // See class documentation for Widget in widget.h for a note about ownership. - Widget::InitParams::Ownership ownership_; - - // Are we in the destructor? - bool destroying_; - - gfx::NativeCursor cursor_; - - // The saved window state for exiting full screen state. - ui::WindowShowState saved_window_state_; - - scoped_ptr<TooltipManagerAura> tooltip_manager_; - - // Reorders child windows of |window_| associated with a view based on the - // order of the associated views in the widget's view hierarchy. - scoped_ptr<WindowReorderer> window_reorderer_; - - scoped_ptr<aura::WindowTreeHost> host_; - scoped_ptr<wm::FocusController> focus_client_; - scoped_ptr<aura::client::DefaultCaptureClient> capture_client_; - scoped_ptr<aura::client::WindowTreeClient> window_tree_client_; - scoped_ptr<aura::client::ScreenPositionClient> screen_position_client_; - scoped_ptr<aura::client::DispatcherClient> dispatcher_client_; - - // The following factory is used for calls to close the - // NativeWidgetAndroid instance. - base::WeakPtrFactory<NativeWidgetAndroid> close_widget_factory_; - - DISALLOW_COPY_AND_ASSIGN(NativeWidgetAndroid); -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_ANDROID_NATIVE_WIDGET_ANDROID_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc deleted file mode 100644 index 409057da5f0..00000000000 --- a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc +++ /dev/null @@ -1,34 +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/widget/desktop_aura/desktop_dispatcher_client.h" - -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/run_loop.h" -#include "build/build_config.h" - -namespace views { - -DesktopDispatcherClient::DesktopDispatcherClient() { -} - -DesktopDispatcherClient::~DesktopDispatcherClient() { -} - -void DesktopDispatcherClient::PrepareNestedLoopClosures( - base::MessagePumpDispatcher* dispatcher, - base::Closure* run_closure, - base::Closure* quit_closure) { -#if defined(OS_WIN) - scoped_ptr<base::RunLoop> run_loop(new base::RunLoop(dispatcher)); -#else - scoped_ptr<base::RunLoop> run_loop(new base::RunLoop()); -#endif - *quit_closure = run_loop->QuitClosure(); - *run_closure = - base::Bind(&base::RunLoop::Run, base::Owned(run_loop.release())); -} - -} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h deleted file mode 100644 index 3d7f6f42caa..00000000000 --- a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPATCHER_CLIENT_H_ -#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPATCHER_CLIENT_H_ - -#include "base/callback.h" -#include "base/macros.h" -#include "ui/views/views_export.h" -#include "ui/wm/public/dispatcher_client.h" - -namespace views { - -// TODO(erg): I won't lie to you; I have no idea what this is or what it does. -class VIEWS_EXPORT DesktopDispatcherClient - : public aura::client::DispatcherClient { - public: - DesktopDispatcherClient(); - ~DesktopDispatcherClient() override; - - void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher, - base::Closure* run_closure, - base::Closure* quit_closure) override; - - private: - DISALLOW_COPY_AND_ASSIGN(DesktopDispatcherClient); -}; - -} // namespace views - -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPATCHER_CLIENT_H_ 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 3ef11e83603..e9907e2a275 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 @@ -1186,8 +1186,8 @@ void DesktopDragDropClientAuraX11::CreateDragWidget( params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.accept_events = false; - gfx::Point location = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint() - - drag_widget_offset_; + gfx::Point location = + gfx::Screen::GetScreen()->GetCursorScreenPoint() - drag_widget_offset_; params.bounds = gfx::Rect(location, image.size()); widget->set_focus_on_creation(false); widget->set_frame_type(Widget::FRAME_TYPE_FORCE_NATIVE); 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 01c1d875c29..f69b839d73a 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 @@ -32,7 +32,6 @@ #include "ui/views/view_constants_aura.h" #include "ui/views/widget/desktop_aura/desktop_capture_client.h" #include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h" -#include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" #include "ui/views/widget/desktop_aura/desktop_event_client.h" #include "ui/views/widget/desktop_aura/desktop_focus_rules.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" @@ -61,7 +60,6 @@ #if defined(OS_WIN) #include "ui/base/win/shell.h" -#include "ui/gfx/win/dpi.h" #endif DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(VIEWS_EXPORT, @@ -341,11 +339,6 @@ void DesktopNativeWidgetAura::OnHostClosed() { void DesktopNativeWidgetAura::OnDesktopWindowTreeHostDestroyed( aura::WindowTreeHost* host) { - // |dispatcher_| is still valid, but DesktopWindowTreeHost is nearly - // destroyed. Do cleanup here of members DesktopWindowTreeHost may also use. - aura::client::SetDispatcherClient(host->window(), NULL); - dispatcher_client_.reset(); - // We explicitly do NOT clear the cursor client property. Since the cursor // manager is a singleton, it can outlive any window hierarchy, and it's // important that objects attached to this destroying window hierarchy have @@ -483,10 +476,6 @@ void DesktopNativeWidgetAura::InitNativeWidget( aura::client::SetActivationClient(host_->window(), focus_controller); host_->window()->AddPreTargetHandler(focus_controller); - dispatcher_client_.reset(new DesktopDispatcherClient); - aura::client::SetDispatcherClient(host_->window(), - dispatcher_client_.get()); - position_client_.reset(new DesktopScreenPositionClient(host_->window())); drag_drop_client_ = desktop_window_tree_host_->CreateDragDropClient( @@ -689,18 +678,9 @@ gfx::Rect DesktopNativeWidgetAura::GetRestoredBounds() const { void DesktopNativeWidgetAura::SetBounds(const gfx::Rect& bounds) { if (!content_window_) return; - // TODO(ananta) - // This code by default scales the bounds rectangle by 1. - // We could probably get rid of this and similar logic from - // the DesktopNativeWidgetAura::OnWindowTreeHostResized function. - float scale = 1; aura::Window* root = host_->window(); - if (root) { - scale = gfx::Screen::GetScreenFor(root)-> - GetDisplayNearestWindow(root).device_scale_factor(); - } - gfx::Rect bounds_in_pixels = - gfx::ScaleToEnclosingRect(bounds, scale, scale); + gfx::Screen* screen = gfx::Screen::GetScreen(); + gfx::Rect bounds_in_pixels = screen->DIPToScreenRectInWindow(root, bounds); desktop_window_tree_host_->AsWindowTreeHost()->SetBounds(bounds_in_pixels); } @@ -840,9 +820,6 @@ void DesktopNativeWidgetAura::SetOpacity(unsigned char opacity) { desktop_window_tree_host_->SetOpacity(opacity); } -void DesktopNativeWidgetAura::SetUseDragFrame(bool use_drag_frame) { -} - void DesktopNativeWidgetAura::FlashFrame(bool flash_frame) { if (content_window_) desktop_window_tree_host_->FlashFrame(flash_frame); @@ -1069,7 +1046,7 @@ void DesktopNativeWidgetAura::OnScrollEvent(ui::ScrollEvent* event) { return; // Convert unprocessed scroll events into wheel events. - ui::MouseWheelEvent mwe(*static_cast<ui::ScrollEvent*>(event)); + ui::MouseWheelEvent mwe(*event->AsScrollEvent()); native_widget_delegate_->OnMouseEvent(&mwe); if (mwe.handled()) event->SetHandled(); 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 2a756d9fd72..47f514924bf 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 @@ -42,7 +42,6 @@ namespace corewm { class TooltipController; } class DesktopCaptureClient; -class DesktopDispatcherClient; class DesktopEventClient; class DesktopNativeCursorManager; class DesktopWindowTreeHost; @@ -149,7 +148,6 @@ class VIEWS_EXPORT DesktopNativeWidgetAura void SetFullscreen(bool fullscreen) override; bool IsFullscreen() const override; void SetOpacity(unsigned char opacity) override; - void SetUseDragFrame(bool use_drag_frame) override; void FlashFrame(bool flash_frame) override; void RunShellDrag(View* view, const ui::OSExchangeData& data, @@ -256,7 +254,6 @@ class VIEWS_EXPORT DesktopNativeWidgetAura internal::NativeWidgetDelegate* native_widget_delegate_; scoped_ptr<wm::FocusController> focus_client_; - scoped_ptr<DesktopDispatcherClient> dispatcher_client_; scoped_ptr<aura::client::ScreenPositionClient> position_client_; scoped_ptr<aura::client::DragDropClient> drag_drop_client_; scoped_ptr<aura::client::WindowTreeClient> window_tree_client_; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc index 9578951b137..86e60d31fc8 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/macros.h" +#include "base/run_loop.h" #include "build/build_config.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/cursor_client.h" @@ -17,15 +18,17 @@ #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/screen.h" +#include "ui/views/test/native_widget_factory.h" #include "ui/views/test/test_views.h" #include "ui/views/test/test_views_delegate.h" #include "ui/views/test/views_test_base.h" #include "ui/views/test/widget_test.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_delegate.h" -#include "ui/wm/public/dispatcher_client.h" #if defined(OS_WIN) +#include "ui/base/view_prop.h" +#include "ui/base/win/window_event_target.h" #include "ui/views/win/hwnd_util.h" #endif @@ -231,15 +234,10 @@ TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) { widget->Init(params); widget->Show(); - aura::Window* window = widget->GetNativeView(); - aura::Window* root = window->GetRootWindow(); - aura::client::DispatcherClient* client = - aura::client::GetDispatcherClient(root); - // Post a task that terminates the nested loop and destroyes the widget. This // task will be executed from the nested loop initiated with the call to // |RunWithDispatcher()| below. - aura::client::DispatcherRunLoop run_loop(client, NULL); + base::RunLoop run_loop; base::Closure quit_runloop = run_loop.QuitClosure(); message_loop()->PostTask(FROM_HERE, base::Bind(&QuitNestedLoopAndCloseWidget, @@ -434,9 +432,7 @@ TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupRepositionTest) { gfx::Rect new_pos(10, 10, 400, 400); popup_window.owned_window()->SetBoundsInScreen( - new_pos, - gfx::Screen::GetScreenFor( - popup_window.owned_window())->GetDisplayNearestPoint(gfx::Point())); + new_pos, gfx::Screen::GetScreen()->GetDisplayNearestPoint(gfx::Point())); EXPECT_EQ(new_pos, popup_window.top_level_widget()->GetWindowBoundsInScreen()); @@ -493,7 +489,8 @@ void RunCloseWidgetDuringDispatchTest(WidgetTest* test, Widget* widget = new Widget; Widget::InitParams params = test->CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = new PlatformDesktopNativeWidget(widget); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, widget, nullptr); params.bounds = gfx::Rect(0, 0, 50, 100); widget->Init(params); widget->SetContentsView(new CloseWidgetView(last_event_type)); @@ -535,8 +532,8 @@ TEST_F(WidgetTest, WindowMouseModalityTest) { gfx::Rect initial_bounds(0, 0, 500, 500); init_params.bounds = initial_bounds; init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = - new PlatformDesktopNativeWidget(&top_level_widget); + init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl( + init_params, &top_level_widget, nullptr); top_level_widget.Init(init_params); top_level_widget.Show(); EXPECT_TRUE(top_level_widget.IsVisible()); @@ -632,6 +629,32 @@ TEST_F(WidgetTest, WindowModalityActivationTest) { modal_dialog_widget->CloseNow(); } + +// This test validates that sending WM_CHAR/WM_SYSCHAR/WM_SYSDEADCHAR +// messages via the WindowEventTarget interface implemented by the +// HWNDMessageHandler class does not cause a crash due to an unprocessed +// event +TEST_F(WidgetTest, CharMessagesAsKeyboardMessagesDoesNotCrash) { + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, &widget, nullptr); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget.Init(params); + widget.Show(); + + ui::WindowEventTarget* target = + reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue( + widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(), + ui::WindowEventTarget::kWin32InputEventTarget)); + ASSERT_NE(nullptr, target); + bool handled = false; + target->HandleKeyboardMessage(WM_CHAR, 0, 0, &handled); + target->HandleKeyboardMessage(WM_SYSCHAR, 0, 0, &handled); + target->HandleKeyboardMessage(WM_SYSDEADCHAR, 0, 0, &handled); + widget.CloseNow(); +} + #endif // defined(OS_WIN) } // namespace test diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc index a8e088c92c6..4df7b7ffae5 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc @@ -24,7 +24,7 @@ DesktopScreenWin::~DesktopScreenWin() { } //////////////////////////////////////////////////////////////////////////////// -// DesktopScreenWin, gfx::ScreenWin implementation: +// DesktopScreenWin, display::win::ScreenWin implementation: gfx::Display DesktopScreenWin::GetDisplayMatching( const gfx::Rect& match_rect) const { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h index 8add5bdb3f2..085ba321da1 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h @@ -6,18 +6,18 @@ #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_WIN_H_ #include "base/macros.h" -#include "ui/gfx/screen_win.h" +#include "ui/display/win/screen_win.h" #include "ui/views/views_export.h" namespace views { -class VIEWS_EXPORT DesktopScreenWin : public gfx::ScreenWin { +class VIEWS_EXPORT DesktopScreenWin : public display::win::ScreenWin { public: DesktopScreenWin(); ~DesktopScreenWin() override; private: - // Overridden from gfx::ScreenWin: + // Overridden from display::win::ScreenWin: gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override; HWND GetHWNDFromNativeView(gfx::NativeView window) const override; gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override; 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 07bcfca6189..6ffd94a3c89 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -20,6 +20,7 @@ #include "ui/display/util/x11/edid_parser_x11.h" #include "ui/events/platform/platform_event_source.h" #include "ui/gfx/display.h" +#include "ui/gfx/font_render_params.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/native_widget_types.h" @@ -38,9 +39,12 @@ const int64_t kConfigureDelayMs = 500; double GetDeviceScaleFactor() { float device_scale_factor = 1.0f; - if (views::LinuxUI::instance()) + if (views::LinuxUI::instance()) { device_scale_factor = views::LinuxUI::instance()->GetDeviceScaleFactor(); + } else if (gfx::Display::HasForceDeviceScaleFactor()) { + device_scale_factor = gfx::Display::GetForcedDeviceScaleFactor(); + } return device_scale_factor; } @@ -104,9 +108,9 @@ DesktopScreenX11::DesktopScreenX11() RROutputChangeNotifyMask | RRCrtcChangeNotifyMask); - displays_ = BuildDisplaysFromXRandRInfo(); + SetDisplaysInternal(BuildDisplaysFromXRandRInfo()); } else { - displays_ = GetFallbackDisplayList(); + SetDisplaysInternal(GetFallbackDisplayList()); } } @@ -255,7 +259,7 @@ uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) { // static void DesktopScreenX11::UpdateDeviceScaleFactorForTest() { DesktopScreenX11* screen = - static_cast<DesktopScreenX11*>(gfx::Screen::GetNativeScreen()); + static_cast<DesktopScreenX11*>(gfx::Screen::GetScreen()); screen->ConfigureTimerFired(); } @@ -310,7 +314,8 @@ std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { crtc(XRRGetCrtcInfo(xdisplay_, resources.get(), output_info->crtc)); int64_t display_id = -1; - if (!ui::GetDisplayId(output_id, static_cast<uint8_t>(i), &display_id)) { + if (!ui::EDIDParserX11(output_id).GetDisplayId(static_cast<uint8_t>(i), + &display_id)) { // It isn't ideal, but if we can't parse the EDID data, fallback on the // display number. display_id = i; @@ -362,11 +367,17 @@ std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { void DesktopScreenX11::ConfigureTimerFired() { std::vector<gfx::Display> old_displays = displays_; - displays_ = BuildDisplaysFromXRandRInfo(); - + SetDisplaysInternal(BuildDisplaysFromXRandRInfo()); change_notifier_.NotifyDisplaysChanged(old_displays, displays_); } +void DesktopScreenX11::SetDisplaysInternal( + const std::vector<gfx::Display>& displays) { + displays_ = displays; + gfx::SetFontRenderParamsDeviceScaleFactor( + GetPrimaryDisplay().device_scale_factor()); +} + //////////////////////////////////////////////////////////////////////////////// gfx::Screen* CreateDesktopScreen() { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h index c49d4daf28b..e96d983ccb2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h @@ -8,6 +8,7 @@ #include <stdint.h> #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/timer/timer.h" #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/gfx/display_change_notifier.h" @@ -70,6 +71,9 @@ class VIEWS_EXPORT DesktopScreenX11 : public gfx::Screen, // We delay updating the display so we can coalesce events. void ConfigureTimerFired(); + // Updates |displays_| and sets FontRenderParams's scale factor. + void SetDisplaysInternal(const std::vector<gfx::Display>& displays); + Display* xdisplay_; ::Window x_root_window_; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc index c527545c4b0..a2e2d2aed7e 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc @@ -16,6 +16,7 @@ #include "ui/base/x/x11_util.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/display_observer.h" +#include "ui/gfx/font_render_params.h" #include "ui/gfx/x/x11_types.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" @@ -83,9 +84,10 @@ class DesktopScreenX11Test : public views::ViewsTestBase, DesktopScreenX11* screen() { return screen_.get(); } void NotifyDisplaysChanged(const std::vector<gfx::Display>& displays) { - DesktopScreenX11* screen = screen_.get(); - screen->change_notifier_.NotifyDisplaysChanged(screen->displays_, displays); - screen->displays_ = displays; + std::vector<gfx::Display> old_displays = screen_->displays_; + screen_->SetDisplaysInternal(displays); + screen_->change_notifier_.NotifyDisplaysChanged(old_displays, + screen_->displays_); } void ResetDisplayChanges() { @@ -439,6 +441,7 @@ TEST_F(DesktopScreenX11Test, DeviceScaleFactorChange) { displays[0].set_device_scale_factor(2.5f); NotifyDisplaysChanged(displays); EXPECT_EQ(1u, changed_display_.size()); + EXPECT_EQ(2.5f, gfx::GetFontRenderParamsDeviceScaleFactor()); displays[1].set_device_scale_factor(2.5f); NotifyDisplaysChanged(displays); @@ -456,6 +459,7 @@ TEST_F(DesktopScreenX11Test, DeviceScaleFactorChange) { displays[1].set_device_scale_factor(1.f); NotifyDisplaysChanged(displays); EXPECT_EQ(4u, changed_display_.size()); + EXPECT_EQ(1.f, gfx::GetFontRenderParamsDeviceScaleFactor()); } } // namespace views 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 c354154a542..fa9ea48b500 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 @@ -125,6 +125,7 @@ void DesktopWindowTreeHostWin::Init(aura::Window* content_window, const Widget::InitParams& params) { // TODO(beng): SetInitParams(). content_window_ = content_window; + wants_mouse_events_when_inactive_ = params.wants_mouse_events_when_inactive; aura::client::SetAnimationHost(content_window_, this); @@ -136,8 +137,7 @@ void DesktopWindowTreeHostWin::Init(aura::Window* content_window, if (params.parent && params.parent->GetHost()) parent_hwnd = params.parent->GetHost()->GetAcceleratedWidget(); - message_handler_->set_remove_standard_frame(params.remove_standard_frame); - + remove_standard_frame_ = params.remove_standard_frame; has_non_client_view_ = Widget::RequiresNonClientView(params.type); gfx::Rect pixel_bounds = gfx::win::DIPToScreenRect(params.bounds); @@ -587,24 +587,30 @@ void DesktopWindowTreeHostWin::OnWindowHidingAnimationCompleted() { //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostWin, HWNDMessageHandlerDelegate implementation: -bool DesktopWindowTreeHostWin::IsWidgetWindow() const { +bool DesktopWindowTreeHostWin::HasNonClientView() const { return has_non_client_view_; } -bool DesktopWindowTreeHostWin::IsUsingCustomFrame() const { - return !GetWidget()->ShouldUseNativeFrame(); +FrameMode DesktopWindowTreeHostWin::GetFrameMode() const { + return GetWidget()->ShouldUseNativeFrame() ? FrameMode::SYSTEM_DRAWN + : FrameMode::CUSTOM_DRAWN; +} + +bool DesktopWindowTreeHostWin::HasFrame() const { + return !remove_standard_frame_; } void DesktopWindowTreeHostWin::SchedulePaint() { GetWidget()->GetRootView()->SchedulePaint(); } -void DesktopWindowTreeHostWin::EnableInactiveRendering() { - native_widget_delegate_->EnableInactiveRendering(); +void DesktopWindowTreeHostWin::SetAlwaysRenderAsActive( + bool always_render_as_active) { + native_widget_delegate_->SetAlwaysRenderAsActive(always_render_as_active); } -bool DesktopWindowTreeHostWin::IsInactiveRenderingDisabled() { - return native_widget_delegate_->IsInactiveRenderingDisabled(); +bool DesktopWindowTreeHostWin::IsAlwaysRenderAsActive() { + return native_widget_delegate_->IsAlwaysRenderAsActive(); } bool DesktopWindowTreeHostWin::CanResize() const { @@ -626,7 +632,7 @@ bool DesktopWindowTreeHostWin::CanActivate() const { } bool DesktopWindowTreeHostWin::WantsMouseEventsWhenInactive() const { - return false; + return wants_mouse_events_when_inactive_; } bool DesktopWindowTreeHostWin::WidgetSizeIsClientSize() const { @@ -692,7 +698,7 @@ bool DesktopWindowTreeHostWin::ShouldHandleSystemCommands() const { } void DesktopWindowTreeHostWin::HandleAppDeactivated() { - native_widget_delegate_->EnableInactiveRendering(); + native_widget_delegate_->SetAlwaysRenderAsActive(false); } void DesktopWindowTreeHostWin::HandleActivationChanged(bool active) { @@ -911,9 +917,11 @@ void DesktopWindowTreeHostWin::HandleWindowSizeChanged() { // changed (can occur on Windows 10 when snapping a window to the side of // the screen). In that case do a resize to the current size to reenable // swaps. - if (compositor()) - compositor()->SetScaleAndSize(compositor()->device_scale_factor(), - compositor()->size()); + if (compositor()) { + compositor()->SetScaleAndSize( + compositor()->device_scale_factor(), + message_handler_->GetClientAreaBounds().size()); + } } //////////////////////////////////////////////////////////////////////////////// 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 ad82485eb0c..3465112eac0 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 @@ -126,11 +126,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin void OnWindowHidingAnimationCompleted() override; // Overridden from HWNDMessageHandlerDelegate: - bool IsWidgetWindow() const override; - bool IsUsingCustomFrame() const override; + bool HasNonClientView() const override; + FrameMode GetFrameMode() const override; + bool HasFrame() const override; void SchedulePaint() override; - void EnableInactiveRendering() override; - bool IsInactiveRenderingDisabled() override; + void SetAlwaysRenderAsActive(bool always_render_as_active) override; + bool IsAlwaysRenderAsActive() override; bool CanResize() const override; bool CanMaximize() const override; bool CanMinimize() const override; @@ -244,6 +245,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin // Init time, before the Widget has created the NonClientView. bool has_non_client_view_; + // True if the window should have the frame removed. + bool remove_standard_frame_; + // Owned by TooltipController, but we need to forward events to it so we keep // a reference. corewm::TooltipWin* tooltip_; @@ -256,6 +260,10 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin scoped_ptr<aura::client::ScopedTooltipDisabler> tooltip_disabler_; + // Indicates if current window will receive mouse events when should not + // become activated. + bool wants_mouse_events_when_inactive_ = false; + DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostWin); }; 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 e2fb724b914..cecae1ee81d 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 @@ -109,7 +109,7 @@ const char* kAtomsToCache[] = { "_NET_WM_WINDOW_TYPE_NOTIFICATION", "_NET_WM_WINDOW_TYPE_TOOLTIP", "XdndActionAsk", - "XdndActionCopy" + "XdndActionCopy", "XdndActionLink", "XdndActionList", "XdndActionMove", @@ -820,7 +820,7 @@ void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) { if (fullscreen) { restored_bounds_in_pixels_ = bounds_in_pixels_; const gfx::Display display = - gfx::Screen::GetScreenFor(NULL)->GetDisplayNearestWindow(window()); + gfx::Screen::GetScreen()->GetDisplayNearestWindow(window()); bounds_in_pixels_ = ToPixelRect(display.bounds()); } else { bounds_in_pixels_ = restored_bounds_in_pixels_; @@ -938,10 +938,10 @@ void DesktopWindowTreeHostX11::SizeConstraintsChanged() { // DesktopWindowTreeHostX11, aura::WindowTreeHost implementation: gfx::Transform DesktopWindowTreeHostX11::GetRootTransform() const { - gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); + gfx::Display display = gfx::Screen::GetScreen()->GetPrimaryDisplay(); if (window_mapped_) { aura::Window* win = const_cast<aura::Window*>(window()); - display = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(win); + display = gfx::Screen::GetScreen()->GetDisplayNearestWindow(win); } float scale = display.device_scale_factor(); @@ -1287,7 +1287,7 @@ void DesktopWindowTreeHostX11::InitX11Window( gfx::Size DesktopWindowTreeHostX11::AdjustSize( const gfx::Size& requested_size_in_pixels) { std::vector<gfx::Display> displays = - gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE)->GetAllDisplays(); + gfx::Screen::GetScreen()->GetAllDisplays(); // Compare against all monitor sizes. The window manager can move the window // to whichever monitor it wants. for (size_t i = 0; i < displays.size(); ++i) { @@ -1533,9 +1533,9 @@ void DesktopWindowTreeHostX11::ConvertEventToDifferentHost( DesktopWindowTreeHostX11* host) { DCHECK_NE(this, host); const gfx::Display display_src = - gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(window()); + gfx::Screen::GetScreen()->GetDisplayNearestWindow(window()); const gfx::Display display_dest = - gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(host->window()); + gfx::Screen::GetScreen()->GetDisplayNearestWindow(host->window()); DCHECK_EQ(display_src.device_scale_factor(), display_dest.device_scale_factor()); gfx::Vector2d offset = GetLocationOnNativeScreen() - 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 759194f6b52..7e842fed086 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 @@ -36,7 +36,6 @@ class EventHandler; namespace views { class DesktopDragDropClientAuraX11; -class DesktopDispatcherClient; class DesktopWindowTreeHostObserverX11; class X11DesktopWindowMoveClient; class X11WindowEventFilter; 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 162cf3c49a3..7c2253493ac 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 @@ -24,7 +24,7 @@ #include "ui/base/hit_test.h" #include "ui/base/x/x11_util.h" #include "ui/events/devices/x11/touch_factory_x11.h" -#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/events/platform/x11/x11_event_source_glib.h" #include "ui/events/test/platform_event_source_test_api.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" @@ -68,11 +68,9 @@ class WMStateWaiter : public X11PropertyChangeWaiter { bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override { std::vector<Atom> hints; if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) { - std::vector<Atom>::iterator it = std::find( - hints.begin(), - hints.end(), - atom_cache_->GetAtom(hint_)); - bool hint_set = (it != hints.end()); + auto it = std::find(hints.cbegin(), hints.cend(), + atom_cache_->GetAtom(hint_)); + bool hint_set = (it != hints.cend()); return hint_set != wait_till_set_; } return true; 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 f5343ad5a2b..46357f60f00 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 @@ -55,11 +55,9 @@ class MinimizeWaiter : public X11PropertyChangeWaiter { bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override { std::vector<Atom> wm_states; if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) { - std::vector<Atom>::iterator it = std::find( - wm_states.begin(), - wm_states.end(), - atom_cache_->GetAtom("_NET_WM_STATE_HIDDEN")); - return it == wm_states.end(); + auto it = std::find(wm_states.cbegin(), wm_states.cend(), + atom_cache_->GetAtom("_NET_WM_STATE_HIDDEN")); + return it == wm_states.cend(); } return true; } @@ -97,9 +95,8 @@ class StackingClientListWaiter : public X11PropertyChangeWaiter { std::vector<XID> stack; ui::GetXWindowStack(ui::GetX11RootWindow(), &stack); for (size_t i = 0; i < expected_windows_.size(); ++i) { - std::vector<XID>::iterator it = std::find( - stack.begin(), stack.end(), expected_windows_[i]); - if (it == stack.end()) + auto it = std::find(stack.cbegin(), stack.cend(), expected_windows_[i]); + if (it == stack.cend()) return true; } return false; diff --git a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc index a33f12ff76e..eca9951d2da 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc @@ -22,6 +22,7 @@ #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/scoped_event_dispatcher.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/views/widget/desktop_aura/x11_pointer_grab.h" @@ -81,7 +82,7 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { case ui::ET_MOUSE_DRAGGED: { bool dispatch_mouse_event = !last_motion_in_screen_.get(); last_motion_in_screen_.reset( - static_cast<ui::MouseEvent*>(ui::EventFromNative(xev).release())); + ui::EventFromNative(xev).release()->AsMouseEvent()); last_motion_in_screen_->set_location( ui::EventSystemLocationFromNative(xev)); if (dispatch_mouse_event) { diff --git a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc index c8f1065d31e..c0ead5563fd 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc @@ -151,9 +151,8 @@ void X11WindowEventFilter::OnClickedMaximizeButton(ui::MouseEvent* event) { if (!widget) return; - gfx::Screen* screen = gfx::Screen::GetNativeScreen(); gfx::Rect display_work_area = - screen->GetDisplayNearestWindow(target).work_area(); + gfx::Screen::GetScreen()->GetDisplayNearestWindow(target).work_area(); gfx::Rect bounds = widget->GetWindowBoundsInScreen(); if (event->IsMiddleMouseButton()) { bounds.set_y(display_work_area.y()); diff --git a/chromium/ui/views/widget/desktop_widget_unittest.cc b/chromium/ui/views/widget/desktop_widget_unittest.cc index 6bd67c6ee4a..0004ccab265 100644 --- a/chromium/ui/views/widget/desktop_widget_unittest.cc +++ b/chromium/ui/views/widget/desktop_widget_unittest.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 "ui/views/test/native_widget_factory.h" #include "ui/views/test/views_test_base.h" #include "ui/views/test/widget_test.h" #include "ui/views/widget/widget.h" @@ -18,7 +19,8 @@ TEST_F(DesktopScreenPositionClientTest, PositionDialog) { Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); params.bounds = gfx::Rect(10, 11, 200, 200); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.native_widget = new PlatformDesktopNativeWidget(&parent_widget); + params.native_widget = test::CreatePlatformDesktopNativeWidgetImpl( + params, &parent_widget, nullptr); parent_widget.Init(params); // Owned by |dialog|. @@ -52,7 +54,8 @@ TEST_F(DesktopScreenPositionClientTest, PositionControlWithNonRootParent) { CreateParams(Widget::InitParams::TYPE_WINDOW); params1.bounds = gfx::Rect(origin, gfx::Size(700, 600)); params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params1.native_widget = new PlatformDesktopNativeWidget(&widget1); + params1.native_widget = + test::CreatePlatformDesktopNativeWidgetImpl(params1, &widget1, nullptr); widget1.Init(params1); Widget::InitParams params2 = diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index fb2396c8399..4f24e5caced 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -137,8 +137,8 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { // If a parent is specified but no bounds are given, // use the origin of the parent's display so that the widget // will be added to the same display as the parent. - gfx::Rect bounds = gfx::Screen::GetScreenFor(parent)-> - GetDisplayNearestWindow(parent).bounds(); + gfx::Rect bounds = + gfx::Screen::GetScreen()->GetDisplayNearestWindow(parent).bounds(); window_bounds.set_origin(bounds.origin()); } } @@ -283,8 +283,8 @@ void NativeWidgetAura::CenterWindow(const gfx::Size& size) { // When centering window, we take the intersection of the host and // the parent. We assume the root window represents the visible // rect of a single screen. - gfx::Rect work_area = gfx::Screen::GetScreenFor(window_)-> - GetDisplayNearestWindow(window_).work_area(); + gfx::Rect work_area = + gfx::Screen::GetScreen()->GetDisplayNearestWindow(window_).work_area(); aura::client::ScreenPositionClient* screen_position_client = aura::client::GetScreenPositionClient(window_->GetRootWindow()); @@ -404,7 +404,7 @@ void NativeWidgetAura::SetBounds(const gfx::Rect& bounds) { aura::client::GetScreenPositionClient(root); if (screen_position_client) { gfx::Display dst_display = - gfx::Screen::GetScreenFor(window_)->GetDisplayMatching(bounds); + gfx::Screen::GetScreen()->GetDisplayMatching(bounds); screen_position_client->SetBounds(window_, bounds, dst_display); return; } @@ -495,7 +495,10 @@ void NativeWidgetAura::ShowWithWindowState(ui::WindowShowState state) { // SetInitialFocus() should be always be called, even for // SHOW_STATE_INACTIVE. If the window has to stay inactive, the method will // do the right thing. - SetInitialFocus(state); + // Activate() might fail if the window is non-activatable. In this case, we + // should pass SHOW_STATE_INACTIVE to SetInitialFocus() to stop the initial + // focused view from getting focused. See crbug.com/515594 for example. + SetInitialFocus(IsActive() ? state : ui::SHOW_STATE_INACTIVE); } // On desktop aura, a window is activated first even when it is shown as @@ -595,10 +598,6 @@ void NativeWidgetAura::SetOpacity(unsigned char opacity) { window_->layer()->SetOpacity(opacity / 255.0); } -void NativeWidgetAura::SetUseDragFrame(bool use_drag_frame) { - NOTIMPLEMENTED(); -} - void NativeWidgetAura::FlashFrame(bool flash) { if (window_) window_->SetProperty(aura::client::kDrawAttentionKey, flash); @@ -643,8 +642,7 @@ void NativeWidgetAura::ClearNativeFocus() { gfx::Rect NativeWidgetAura::GetWorkAreaBoundsInScreen() const { if (!window_) return gfx::Rect(); - return gfx::Screen::GetScreenFor(window_)-> - GetDisplayNearestWindow(window_).work_area(); + return gfx::Screen::GetScreen()->GetDisplayNearestWindow(window_).work_area(); } Widget::MoveLoopResult NativeWidgetAura::RunMoveLoop( @@ -875,7 +873,6 @@ void NativeWidgetAura::OnKeyEvent(ui::KeyEvent* event) { return; delegate_->OnKeyEvent(event); - event->SetHandled(); } void NativeWidgetAura::OnMouseEvent(ui::MouseEvent* event) { diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h index 30bf169c784..9e27cca806b 100644 --- a/chromium/ui/views/widget/native_widget_aura.h +++ b/chromium/ui/views/widget/native_widget_aura.h @@ -42,8 +42,8 @@ class VIEWS_EXPORT NativeWidgetAura public: explicit NativeWidgetAura(internal::NativeWidgetDelegate* delegate); - // Called internally by NativeWidget implementations to associate - // |native_widget| with |window|. + // Called internally by NativeWidgetAura and DesktopNativeWidgetAura to + // associate |native_widget| with |window|. static void RegisterNativeWidgetForWindow( internal::NativeWidgetPrivate* native_widget, aura::Window* window); @@ -108,7 +108,6 @@ class VIEWS_EXPORT NativeWidgetAura void SetFullscreen(bool fullscreen) override; bool IsFullscreen() const override; void SetOpacity(unsigned char opacity) override; - void SetUseDragFrame(bool use_drag_frame) override; void FlashFrame(bool flash_frame) override; void RunShellDrag(View* view, const ui::OSExchangeData& data, diff --git a/chromium/ui/views/widget/native_widget_aura_unittest.cc b/chromium/ui/views/widget/native_widget_aura_unittest.cc index f10981f2327..0a626ae45a1 100644 --- a/chromium/ui/views/widget/native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/native_widget_aura_unittest.cc @@ -19,9 +19,12 @@ #include "ui/events/event_utils.h" #include "ui/gfx/screen.h" #include "ui/views/layout/fill_layout.h" +#include "ui/views/test/widget_test.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget_delegate.h" +#include "ui/wm/core/base_focus_rules.h" #include "ui/wm/core/default_activation_client.h" +#include "ui/wm/core/focus_controller.h" namespace views { namespace { @@ -34,19 +37,50 @@ NativeWidgetAura* Init(aura::Window* parent, Widget* widget) { return static_cast<NativeWidgetAura*>(widget->native_widget()); } +// TestFocusRules is intended to provide a way to manually set a window's +// activatability so that the focus rules can be tested. +class TestFocusRules : public wm::BaseFocusRules { + public: + TestFocusRules() {} + ~TestFocusRules() override {} + + void set_can_activate(bool can_activate) { can_activate_ = can_activate; } + + // wm::BaseFocusRules overrides: + bool SupportsChildActivation(aura::Window* window) const override { + return true; + } + + bool CanActivateWindow(aura::Window* window) const override { + return can_activate_; + } + + private: + bool can_activate_ = true; + + DISALLOW_COPY_AND_ASSIGN(TestFocusRules); +}; + class NativeWidgetAuraTest : public aura::test::AuraTestBase { public: NativeWidgetAuraTest() {} ~NativeWidgetAuraTest() override {} + TestFocusRules* test_focus_rules() { return test_focus_rules_; } + // testing::Test overrides: void SetUp() override { AuraTestBase::SetUp(); - new wm::DefaultActivationClient(root_window()); + test_focus_rules_ = new TestFocusRules; + focus_controller_.reset(new wm::FocusController(test_focus_rules_)); + aura::client::SetActivationClient(root_window(), focus_controller_.get()); host()->SetBounds(gfx::Rect(640, 480)); } private: + scoped_ptr<wm::FocusController> focus_controller_; + TestFocusRules* test_focus_rules_; + DISALLOW_COPY_AND_ASSIGN(NativeWidgetAuraTest); }; @@ -485,5 +519,20 @@ TEST_F(NativeWidgetAuraTest, OnWidgetMovedInvokedAfterAcquireLayer) { widget->CloseNow(); } +// Tests that if a widget has a view which should be initially focused when the +// widget is shown, this view should not get focused if the associated window +// can not be activated. +TEST_F(NativeWidgetAuraTest, PreventFocusOnNonActivableWindow) { + test_focus_rules()->set_can_activate(false); + views::test::TestInitialFocusWidgetDelegate delegate(root_window()); + delegate.GetWidget()->Show(); + EXPECT_FALSE(delegate.view()->HasFocus()); + + test_focus_rules()->set_can_activate(true); + views::test::TestInitialFocusWidgetDelegate delegate2(root_window()); + delegate2.GetWidget()->Show(); + EXPECT_TRUE(delegate2.view()->HasFocus()); +} + } // namespace } // namespace views diff --git a/chromium/ui/views/widget/native_widget_delegate.h b/chromium/ui/views/widget/native_widget_delegate.h index 224972c2d12..033db2bf107 100644 --- a/chromium/ui/views/widget/native_widget_delegate.h +++ b/chromium/ui/views/widget/native_widget_delegate.h @@ -51,8 +51,12 @@ class VIEWS_EXPORT NativeWidgetDelegate { // Returns true if the window can be activated. virtual bool CanActivate() const = 0; - virtual bool IsInactiveRenderingDisabled() const = 0; - virtual void EnableInactiveRendering() = 0; + // Prevents the window from being rendered as deactivated. This state is + // reset automatically as soon as the window becomes activated again. There is + // no ability to control the state through this API as this leads to sync + // problems. + virtual void SetAlwaysRenderAsActive(bool always_render_as_active) = 0; + virtual bool IsAlwaysRenderAsActive() const = 0; // Called when the activation state of a window has changed. virtual void OnNativeWidgetActivationChanged(bool active) = 0; @@ -113,11 +117,10 @@ class VIEWS_EXPORT NativeWidgetDelegate { // |point|, in client coordinates. virtual int GetNonClientComponent(const gfx::Point& point) = 0; - // Mouse and key event handlers. + // Event handlers. virtual void OnKeyEvent(ui::KeyEvent* event) = 0; virtual void OnMouseEvent(ui::MouseEvent* event) = 0; virtual void OnMouseCaptureLost() = 0; - virtual void OnScrollEvent(ui::ScrollEvent* event) = 0; virtual void OnGestureEvent(ui::GestureEvent* event) = 0; @@ -133,7 +136,6 @@ class VIEWS_EXPORT NativeWidgetDelegate { // Provides the hit-test mask if HasHitTestMask above returns true. virtual void GetHitTestMask(gfx::Path* mask) const = 0; - // virtual Widget* AsWidget() = 0; virtual const Widget* AsWidget() const = 0; diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h index f2f79181a05..1b7deb2c801 100644 --- a/chromium/ui/views/widget/native_widget_mac.h +++ b/chromium/ui/views/widget/native_widget_mac.h @@ -107,7 +107,6 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { void SetFullscreen(bool fullscreen) override; bool IsFullscreen() const override; void SetOpacity(unsigned char opacity) override; - void SetUseDragFrame(bool use_drag_frame) override; void FlashFrame(bool flash_frame) override; void RunShellDrag(View* view, const ui::OSExchangeData& data, diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm index f61ff8171a5..126623ff0e7 100644 --- a/chromium/ui/views/widget/native_widget_mac.mm +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -193,8 +193,10 @@ const ui::Layer* NativeWidgetMac::GetLayer() const { } void NativeWidgetMac::ReorderNativeViews() { - if (bridge_) + if (bridge_) { bridge_->SetRootView(GetWidget()->GetRootView()); + bridge_->ReorderChildViews(); + } } void NativeWidgetMac::ViewRemoved(View* view) { @@ -410,6 +412,10 @@ void NativeWidgetMac::ShowWithWindowState(ui::WindowShowState state) { bridge_->SetVisibilityState(state == ui::SHOW_STATE_INACTIVE ? BridgedNativeWidget::SHOW_INACTIVE : BridgedNativeWidget::SHOW_AND_ACTIVATE_WINDOW); + + // Ignore the SetInitialFocus() result. BridgedContentView should get + // firstResponder status regardless. + delegate_->SetInitialFocus(state); } bool NativeWidgetMac::IsVisible() const { @@ -484,11 +490,7 @@ bool NativeWidgetMac::IsFullscreen() const { } void NativeWidgetMac::SetOpacity(unsigned char opacity) { - NOTIMPLEMENTED(); -} - -void NativeWidgetMac::SetUseDragFrame(bool use_drag_frame) { - NOTIMPLEMENTED(); + [GetNativeWindow() setAlphaValue:opacity / 255.0]; } void NativeWidgetMac::FlashFrame(bool flash_frame) { @@ -504,9 +506,15 @@ void NativeWidgetMac::RunShellDrag(View* view, } void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { - // TODO(tapted): This should use setNeedsDisplayInRect:, once the coordinate - // system of |rect| has been converted. - [GetNativeView() setNeedsDisplay:YES]; + // |rect| is relative to client area of the window. + NSWindow* window = GetNativeWindow(); + NSRect client_rect = [window contentRectForFrameRect:[window frame]]; + NSRect target_rect = rect.ToCGRect(); + + // Convert to Appkit coordinate system (origin at bottom left). + target_rect.origin.y = + NSHeight(client_rect) - target_rect.origin.y - NSHeight(target_rect); + [GetNativeView() setNeedsDisplayInRect:target_rect]; if (bridge_ && bridge_->layer()) bridge_->layer()->SchedulePaint(rect); } 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 8f192662b24..528f67d42a7 100644 --- a/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm +++ b/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm @@ -6,8 +6,10 @@ #import <Cocoa/Cocoa.h> +#import "base/mac/mac_util.h" #import "base/mac/scoped_nsobject.h" #include "base/macros.h" +#include "ui/base/test/ui_controls.h" #import "ui/base/test/windowed_nsnotification_observer.h" #include "ui/views/test/test_widget_observer.h" #include "ui/views/test/widget_test.h" @@ -24,7 +26,11 @@ class NativeWidgetMacInteractiveUITest class Observer; NativeWidgetMacInteractiveUITest() - : activationCount_(0), deactivationCount_(0) {} + : activationCount_(0), deactivationCount_(0) { + // TODO(tapted): Remove this when these are absorbed into Chrome's + // interactive_ui_tests target. See http://crbug.com/403679. + ui_controls::EnableUIControls(); + } Widget* MakeWidget() { return GetParam() ? CreateTopLevelFramelessPlatformWidget() @@ -133,6 +139,79 @@ TEST_P(NativeWidgetMacInteractiveUITest, ShowInactiveIgnoresKeyStatus) { widget->CloseNow(); } +namespace { + +// Show |widget| and wait for it to become the key window. +void ShowKeyWindow(Widget* widget) { + base::scoped_nsobject<WindowedNSNotificationObserver> waiter( + [[WindowedNSNotificationObserver alloc] + initForNotification:NSWindowDidBecomeKeyNotification + object:widget->GetNativeWindow()]); + widget->Show(); + EXPECT_TRUE([waiter wait]); + EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]); +} + +NSData* ViewAsTIFF(NSView* view) { + NSBitmapImageRep* bitmap = + [view bitmapImageRepForCachingDisplayInRect:[view bounds]]; + [view cacheDisplayInRect:[view bounds] toBitmapImageRep:bitmap]; + return [bitmap TIFFRepresentation]; +} + +} // namespace + +// Test that parent windows keep their traffic lights enabled when showing +// dialogs. +TEST_F(NativeWidgetMacInteractiveUITest, ParentWindowTrafficLights) { + Widget* parent_widget = CreateTopLevelPlatformWidget(); + parent_widget->SetBounds(gfx::Rect(100, 100, 100, 100)); + ShowKeyWindow(parent_widget); + + NSWindow* parent = parent_widget->GetNativeWindow(); + EXPECT_TRUE([parent isMainWindow]); + + NSButton* button = [parent standardWindowButton:NSWindowCloseButton]; + EXPECT_TRUE(button); + NSData* active_button_image = ViewAsTIFF(button); + EXPECT_TRUE(active_button_image); + + // Create an activatable frameless child. Frameless so that it doesn't have + // traffic lights of its own, and activatable so that it can take key status. + Widget* child_widget = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.native_widget = new NativeWidgetMac(child_widget); + params.bounds = gfx::Rect(130, 130, 100, 100); + params.parent = parent_widget->GetNativeView(); + child_widget->Init(params); + ShowKeyWindow(child_widget); + + // Ensure the button instance is still valid. + EXPECT_EQ(button, [parent standardWindowButton:NSWindowCloseButton]); + + // Parent window should still be main, and have its traffic lights active. + EXPECT_TRUE([parent isMainWindow]); + EXPECT_FALSE([parent isKeyWindow]); + + // Enabled status doesn't actually change, but check anyway. + EXPECT_TRUE([button isEnabled]); + NSData* button_image_with_child = ViewAsTIFF(button); + EXPECT_TRUE([active_button_image isEqualToData:button_image_with_child]); + + // Verify that activating some other random window does change the button. + Widget* other_widget = CreateTopLevelPlatformWidget(); + other_widget->SetBounds(gfx::Rect(200, 200, 100, 100)); + ShowKeyWindow(other_widget); + EXPECT_FALSE([parent isMainWindow]); + EXPECT_FALSE([parent isKeyWindow]); + EXPECT_TRUE([button isEnabled]); + NSData* inactive_button_image = ViewAsTIFF(button); + EXPECT_FALSE([active_button_image isEqualToData:inactive_button_image]); + + other_widget->CloseNow(); + 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 5a48f025722..ba095dfbeeb 100644 --- a/chromium/ui/views/widget/native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm @@ -22,13 +22,17 @@ #import "ui/events/test/cocoa_test_event_utils.h" #include "ui/events/test/event_generator.h" #import "ui/gfx/mac/coordinate_conversion.h" +#include "ui/views/bubble/bubble_delegate.h" #import "ui/views/cocoa/bridged_native_widget.h" #import "ui/views/cocoa/native_widget_mac_nswindow.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" +#include "ui/views/controls/native/native_view_host.h" #include "ui/views/native_cursor.h" +#include "ui/views/test/native_widget_factory.h" #include "ui/views/test/test_widget_observer.h" #include "ui/views/test/widget_test.h" +#include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/window/dialog_delegate.h" @@ -53,6 +57,24 @@ @property(readonly, nonatomic) int invalidateShadowCount; @end +// Used to mock BridgedContentView so that calls to drawRect: can be +// intercepted. +@interface MockBridgedView : NSView { + @private + // Number of times -[NSView drawRect:] has been called. + NSUInteger drawRectCount_; + + // The dirtyRect parameter passed to last invocation of drawRect:. + NSRect lastDirtyRect_; +} + +@property(assign, nonatomic) NSUInteger drawRectCount; +@property(assign, nonatomic) NSRect lastDirtyRect; +@end + +@interface FocusableTestNSView : NSView +@end + namespace views { namespace test { @@ -190,6 +212,30 @@ class WidgetChangeObserver : public TestWidgetObserver { DISALLOW_COPY_AND_ASSIGN(WidgetChangeObserver); }; +class NativeHostHolder { + public: + NativeHostHolder() + : view_([[NSView alloc] init]), host_(new NativeViewHost()) { + host_->set_owned_by_client(); + } + + void AttachNativeView() { + DCHECK(!host_->native_view()); + host_->Attach(view_.get()); + } + + void Detach() { host_->Detach(); } + + gfx::NativeView view() const { return view_.get(); } + NativeViewHost* host() const { return host_.get(); } + + private: + base::scoped_nsobject<NSView> view_; + scoped_ptr<NativeViewHost> host_; + + DISALLOW_COPY_AND_ASSIGN(NativeHostHolder); +}; + // Test visibility states triggered externally. TEST_F(NativeWidgetMacTest, HideAndShowExternally) { Widget* widget = CreateTopLevelPlatformWidget(); @@ -468,8 +514,9 @@ TEST_F(NativeWidgetMacTest, SetCursor) { EXPECT_NE(arrow, hand); EXPECT_NE(arrow, ibeam); - // At the start of the test, the cursor stack should be empty. - EXPECT_FALSE([NSCursor currentCursor]); + // Make arrow the current cursor. + [arrow set]; + EXPECT_EQ(arrow, [NSCursor currentCursor]); // Use an event generator to ask views code to set the cursor. However, note // that this does not cause Cocoa to generate tracking rectangle updates. @@ -477,7 +524,7 @@ TEST_F(NativeWidgetMacTest, SetCursor) { widget->GetNativeWindow()); // Move the mouse over the first view, then simulate a tracking rectangle - // update. + // update. Verify that the cursor changed from arrow to hand type. event_generator.MoveMouseTo(gfx::Point(50, 50)); [widget->GetNativeWindow() cursorUpdate:event_in_content]; EXPECT_EQ(hand, [NSCursor currentCursor]); @@ -534,12 +581,12 @@ TEST_F(NativeWidgetMacTest, NonWidgetParent) { [[native_parent contentView] addSubview:anchor_view]; // Note: Don't use WidgetTest::CreateChildPlatformWidget because that makes - // windows of TYPE_CONTROL which are automatically made visible. But still - // mark it as a child to test window positioning. + // windows of TYPE_CONTROL which need a parent Widget to obtain the focus + // manager. Widget* child = new Widget; Widget::InitParams init_params; init_params.parent = anchor_view; - init_params.child = true; + init_params.type = Widget::InitParams::TYPE_POPUP; child->Init(init_params); TestWidgetObserver child_observer(child); @@ -556,7 +603,8 @@ TEST_F(NativeWidgetMacTest, NonWidgetParent) { NativeWidgetMac::GetBridgeForNativeWindow(child->GetNativeWindow()); EXPECT_EQ(native_parent, bridged_native_widget->parent()->GetNSWindow()); - child->SetBounds(gfx::Rect(50, 50, 200, 100)); + const gfx::Rect child_bounds(50, 50, 200, 100); + child->SetBounds(child_bounds); EXPECT_FALSE(child->IsVisible()); EXPECT_EQ(0u, [[native_parent childWindows] count]); @@ -567,11 +615,9 @@ TEST_F(NativeWidgetMacTest, NonWidgetParent) { [[native_parent childWindows] objectAtIndex:0]); EXPECT_EQ(native_parent, [child->GetNativeWindow() parentWindow]); - // Child should be positioned on screen relative to the parent, but note we - // positioned the parent in Cocoa coordinates, so we need to convert. - gfx::Point parent_origin = gfx::ScreenRectFromNSRect(ParentRect()).origin(); - EXPECT_EQ(gfx::Rect(150, parent_origin.y() + 50, 200, 100), - child->GetWindowBoundsInScreen()); + // Only non-toplevel Widgets are positioned relative to the parent, so the + // bounds set above should be in screen coordinates. + EXPECT_EQ(child_bounds, child->GetWindowBoundsInScreen()); // Removing the anchor_view from its view hierarchy is permitted. This should // not break the relationship between the two windows. @@ -918,6 +964,96 @@ TEST_F(NativeWidgetMacTest, NoopReparentNativeView) { parent_widget->CloseNow(); } +// Attaches a child window to |parent| that checks its parent's delegate is +// cleared when the child is destroyed. This assumes the child is destroyed via +// destruction of its parent. +class ParentCloseMonitor : public WidgetObserver { + public: + explicit ParentCloseMonitor(Widget* parent) { + Widget* child = new Widget(); + child->AddObserver(this); + Widget::InitParams init_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.parent = parent->GetNativeView(); + init_params.bounds = gfx::Rect(100, 100, 100, 100); + init_params.native_widget = + CreatePlatformNativeWidgetImpl(init_params, child, kStubCapture, + nullptr); + child->Init(init_params); + child->Show(); + + // NSWindow parent/child relationship should be established on Show() and + // the parent should have a delegate. Retain the parent since it can't be + // retrieved from the child while it is being destroyed. + parent_nswindow_.reset([[child->GetNativeWindow() parentWindow] retain]); + EXPECT_TRUE(parent_nswindow_); + EXPECT_TRUE([parent_nswindow_ delegate]); + } + + ~ParentCloseMonitor() override { + EXPECT_TRUE(child_closed_); // Otherwise the observer wasn't removed. + } + + void OnWidgetDestroying(Widget* child) override { + // Upon a parent-triggered close, the NSWindow relationship will already be + // removed. The parent should still be open (children are always closed + // first), but not have a delegate (since it is being torn down). + EXPECT_FALSE([child->GetNativeWindow() parentWindow]); + EXPECT_TRUE([parent_nswindow_ isVisible]); + EXPECT_FALSE([parent_nswindow_ delegate]); + + EXPECT_FALSE(child_closed_); + child->RemoveObserver(this); + child_closed_ = true; + } + + bool child_closed() const { return child_closed_; } + + private: + base::scoped_nsobject<NSWindow> parent_nswindow_; + bool child_closed_ = false; + + DISALLOW_COPY_AND_ASSIGN(ParentCloseMonitor); +}; + +// Ensures when a parent window is destroyed, and triggers its child windows to +// be closed, that the child windows (via AppKit) do not attempt to call back +// into the parent, whilst it's in the process of being destroyed. +TEST_F(NativeWidgetMacTest, NoParentDelegateDuringTeardown) { + // First test "normal" windows and AppKit close. + { + Widget* parent = CreateTopLevelPlatformWidget(); + parent->SetBounds(gfx::Rect(100, 100, 300, 200)); + parent->Show(); + ParentCloseMonitor monitor(parent); + [parent->GetNativeWindow() close]; + EXPECT_TRUE(monitor.child_closed()); + } + + // Test the Widget::CloseNow() flow. + { + Widget* parent = CreateTopLevelPlatformWidget(); + parent->SetBounds(gfx::Rect(100, 100, 300, 200)); + parent->Show(); + ParentCloseMonitor monitor(parent); + parent->CloseNow(); + EXPECT_TRUE(monitor.child_closed()); + } + + // Test the WIDGET_OWNS_NATIVE_WIDGET flow. + { + scoped_ptr<Widget> parent(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(100, 100, 300, 200); + parent->Init(params); + parent->Show(); + + ParentCloseMonitor monitor(parent.get()); + parent.reset(); + EXPECT_TRUE(monitor.child_closed()); + } +} + // Tests Cocoa properties that should be given to particular widget types. TEST_F(NativeWidgetMacTest, NativeProperties) { // Create a regular widget (TYPE_WINDOW). @@ -938,6 +1074,20 @@ TEST_F(NativeWidgetMacTest, NativeProperties) { // Dialogs shouldn't take main status away from their parent. EXPECT_FALSE([dialog_widget->GetNativeWindow() canBecomeMainWindow]); + // Create a bubble widget with a parent: also shouldn't get main. + BubbleDelegateView* bubble_view = new BubbleDelegateView(); + bubble_view->set_parent_window(regular_widget->GetNativeView()); + Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_view); + EXPECT_TRUE([bubble_widget->GetNativeWindow() canBecomeKeyWindow]); + EXPECT_FALSE([bubble_widget->GetNativeWindow() canBecomeMainWindow]); + + // But a bubble without a parent should still be able to become main. + Widget* toplevel_bubble_widget = + BubbleDelegateView::CreateBubble(new BubbleDelegateView()); + EXPECT_TRUE([toplevel_bubble_widget->GetNativeWindow() canBecomeKeyWindow]); + EXPECT_TRUE([toplevel_bubble_widget->GetNativeWindow() canBecomeMainWindow]); + + toplevel_bubble_widget->CloseNow(); regular_widget->CloseNow(); } @@ -991,7 +1141,8 @@ TEST_F(NativeWidgetMacTest, DoesHideTitle) { // Same as CreateTopLevelPlatformWidget but with a custom delegate. Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); Widget* widget = new Widget; - params.native_widget = new NativeWidgetCapture(widget); + params.native_widget = + CreatePlatformNativeWidgetImpl(params, widget, kStubCapture, nullptr); CustomTitleWidgetDelegate delegate(widget); params.delegate = &delegate; params.bounds = gfx::Rect(0, 0, 800, 600); @@ -1089,6 +1240,235 @@ TEST_F(NativeWidgetMacTest, GetWorkAreaBoundsInScreen) { EXPECT_TRUE(NSIsEmptyRect(actual)); } +// Test that Widget opacity can be changed. +TEST_F(NativeWidgetMacTest, ChangeOpacity) { + Widget* widget = CreateTopLevelPlatformWidget(); + NSWindow* ns_window = widget->GetNativeWindow(); + + CGFloat old_opacity = [ns_window alphaValue]; + widget->SetOpacity(0xAA); + EXPECT_NE(old_opacity, [ns_window alphaValue]); + EXPECT_DOUBLE_EQ(0xAA / 255.0, [ns_window alphaValue]); + + widget->CloseNow(); +} + +// Test that NativeWidgetMac::SchedulePaintInRect correctly passes the dirtyRect +// parameter to BridgedContentView::drawRect, for a titled window (window with a +// toolbar). +TEST_F(NativeWidgetMacTest, SchedulePaintInRect_Titled) { + Widget* widget = CreateTopLevelPlatformWidget(); + + gfx::Rect screen_rect(50, 50, 100, 100); + widget->SetBounds(screen_rect); + + // Setup the mock content view for the NSWindow, so that we can intercept + // drawRect. + NSWindow* window = widget->GetNativeWindow(); + base::scoped_nsobject<MockBridgedView> mock_bridged_view( + [[MockBridgedView alloc] init]); + [window setContentView:mock_bridged_view]; + + // Ensure the initial draw of the window is done. + base::RunLoop().RunUntilIdle(); + + // Add a dummy view to the widget. This will cause SchedulePaint to be called + // on the dummy view. + View* dummy_view = new View(); + gfx::Rect dummy_bounds(25, 30, 10, 15); + dummy_view->SetBoundsRect(dummy_bounds); + // Reset drawRect count. + [mock_bridged_view setDrawRectCount:0]; + widget->GetContentsView()->AddChildView(dummy_view); + + // SchedulePaint is asyncronous. Wait for drawRect: to be called. + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1u, [mock_bridged_view drawRectCount]); + int client_area_height = widget->GetClientAreaBoundsInScreen().height(); + // These are expected dummy_view bounds in AppKit coordinate system. The y + // coordinate of rect origin is calculated as: + // client_area_height - 30 (dummy_view's y coordinate) - 15 (dummy view's + // height). + gfx::Rect expected_appkit_bounds(25, client_area_height - 45, 10, 15); + EXPECT_NSEQ(expected_appkit_bounds.ToCGRect(), + [mock_bridged_view lastDirtyRect]); + widget->CloseNow(); +} + +// Test that NativeWidgetMac::SchedulePaintInRect correctly passes the dirtyRect +// parameter to BridgedContentView::drawRect, for a borderless window. +TEST_F(NativeWidgetMacTest, SchedulePaintInRect_Borderless) { + Widget* widget = CreateTopLevelFramelessPlatformWidget(); + + gfx::Rect screen_rect(50, 50, 100, 100); + widget->SetBounds(screen_rect); + + // Setup the mock content view for the NSWindow, so that we can intercept + // drawRect. + NSWindow* window = widget->GetNativeWindow(); + base::scoped_nsobject<MockBridgedView> mock_bridged_view( + [[MockBridgedView alloc] init]); + [window setContentView:mock_bridged_view]; + + // Ensure the initial draw of the window is done. + base::RunLoop().RunUntilIdle(); + + // Add a dummy view to the widget. This will cause SchedulePaint to be called + // on the dummy view. + View* dummy_view = new View(); + gfx::Rect dummy_bounds(25, 30, 10, 15); + dummy_view->SetBoundsRect(dummy_bounds); + // Reset drawRect count. + [mock_bridged_view setDrawRectCount:0]; + widget->GetRootView()->AddChildView(dummy_view); + + // SchedulePaint is asyncronous. Wait for drawRect: to be called. + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1u, [mock_bridged_view drawRectCount]); + // These are expected dummy_view bounds in AppKit coordinate system. The y + // coordinate of rect origin is calculated as: + // 100(client area height) - 30 (dummy_view's y coordinate) - 15 (dummy view's + // height). + gfx::Rect expected_appkit_bounds(25, 55, 10, 15); + EXPECT_NSEQ(expected_appkit_bounds.ToCGRect(), + [mock_bridged_view lastDirtyRect]); + widget->CloseNow(); +} + +// Ensure traversing NSView focus correctly updates the views::FocusManager. +TEST_F(NativeWidgetMacTest, ChangeFocusOnChangeFirstResponder) { + Widget* widget = CreateTopLevelPlatformWidget(); + widget->GetRootView()->SetFocusable(true); + widget->Show(); + + base::scoped_nsobject<NSView> child_view([[FocusableTestNSView alloc] + initWithFrame:[widget->GetNativeView() bounds]]); + [widget->GetNativeView() addSubview:child_view]; + EXPECT_TRUE([child_view acceptsFirstResponder]); + EXPECT_TRUE(widget->GetRootView()->IsFocusable()); + + FocusManager* manager = widget->GetFocusManager(); + manager->SetFocusedView(widget->GetRootView()); + EXPECT_EQ(manager->GetFocusedView(), widget->GetRootView()); + + [widget->GetNativeWindow() makeFirstResponder:child_view]; + EXPECT_FALSE(manager->GetFocusedView()); + + [widget->GetNativeWindow() makeFirstResponder:widget->GetNativeView()]; + EXPECT_EQ(manager->GetFocusedView(), widget->GetRootView()); + + widget->CloseNow(); +} + +class NativeWidgetMacViewsOrderTest : public WidgetTest { + public: + NativeWidgetMacViewsOrderTest() {} + + protected: + // testing::Test: + void SetUp() override { + WidgetTest::SetUp(); + + widget_ = CreateTopLevelPlatformWidget(); + + ASSERT_EQ(1u, [[widget_->GetNativeView() subviews] count]); + compositor_view_ = [[widget_->GetNativeView() subviews] firstObject]; + + native_host_parent_ = new View(); + widget_->GetContentsView()->AddChildView(native_host_parent_); + + const int kNativeViewCount = 3; + for (int i = 0; i < kNativeViewCount; ++i) { + scoped_ptr<NativeHostHolder> holder(new NativeHostHolder()); + native_host_parent_->AddChildView(holder->host()); + holder->AttachNativeView(); + hosts_.push_back(std::move(holder)); + } + EXPECT_EQ(kNativeViewCount, native_host_parent_->child_count()); + EXPECT_TRUE(([[widget_->GetNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() + ]])); + } + + void TearDown() override { + widget_->CloseNow(); + WidgetTest::TearDown(); + } + + NSView* GetContentNativeView() { return widget_->GetNativeView(); } + + Widget* widget_ = nullptr; + View* native_host_parent_ = nullptr; + NSView* compositor_view_ = nil; + std::vector<scoped_ptr<NativeHostHolder>> hosts_; + + private: + DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacViewsOrderTest); +}; + +// Test that NativeViewHost::Attach()/Detach() method saves the NativeView +// z-order. +TEST_F(NativeWidgetMacViewsOrderTest, NativeViewAttached) { + hosts_[1]->Detach(); + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[2]->view() + ]])); + + hosts_[1]->AttachNativeView(); + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[1]->view(), + hosts_[2]->view() + ]])); +} + +// Tests that NativeViews order changes according to views::View hierarchy. +TEST_F(NativeWidgetMacViewsOrderTest, ReorderViews) { + native_host_parent_->ReorderChildView(hosts_[2]->host(), 1); + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[2]->view(), + hosts_[1]->view() + ]])); + + native_host_parent_->RemoveChildView(hosts_[2]->host()); + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[1]->view() + ]])); + + View* new_parent = new View(); + native_host_parent_->RemoveChildView(hosts_[1]->host()); + native_host_parent_->AddChildView(new_parent); + new_parent->AddChildView(hosts_[1]->host()); + new_parent->AddChildView(hosts_[2]->host()); + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[1]->view(), + hosts_[2]->view() + ]])); + + native_host_parent_->ReorderChildView(new_parent, 0); + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[1]->view(), hosts_[2]->view(), + hosts_[0]->view() + ]])); +} + +// Test that unassociated native views stay on top after reordering. +TEST_F(NativeWidgetMacViewsOrderTest, UnassociatedViewsIsAbove) { + base::scoped_nsobject<NSView> child_view([[NSView alloc] init]); + [GetContentNativeView() addSubview:child_view]; + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[1]->view(), + hosts_[2]->view(), child_view + ]])); + + native_host_parent_->ReorderChildView(hosts_[2]->host(), 1); + EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ + compositor_view_, hosts_[0]->view(), hosts_[2]->view(), + hosts_[1]->view(), child_view + ]])); +} + } // namespace test } // namespace views @@ -1108,3 +1488,21 @@ TEST_F(NativeWidgetMacTest, GetWorkAreaBoundsInScreen) { } @end + +@implementation MockBridgedView + +@synthesize drawRectCount = drawRectCount_; +@synthesize lastDirtyRect = lastDirtyRect_; + +- (void)drawRect:(NSRect)dirtyRect { + ++drawRectCount_; + lastDirtyRect_ = dirtyRect; +} + +@end + +@implementation FocusableTestNSView +- (BOOL)acceptsFirstResponder { + return YES; +} +@end diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h index 6e52bf66749..777eaa226a4 100644 --- a/chromium/ui/views/widget/native_widget_private.h +++ b/chromium/ui/views/widget/native_widget_private.h @@ -194,7 +194,6 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual void SetFullscreen(bool fullscreen) = 0; virtual bool IsFullscreen() const = 0; virtual void SetOpacity(unsigned char opacity) = 0; - virtual void SetUseDragFrame(bool use_drag_frame) = 0; virtual void FlashFrame(bool flash) = 0; virtual void RunShellDrag(View* view, const ui::OSExchangeData& data, diff --git a/chromium/ui/views/widget/tooltip_manager.h b/chromium/ui/views/widget/tooltip_manager.h index 4287fc53d6a..e8074047340 100644 --- a/chromium/ui/views/widget/tooltip_manager.h +++ b/chromium/ui/views/widget/tooltip_manager.h @@ -8,7 +8,6 @@ #include <string> #include "base/strings/string16.h" -#include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" namespace gfx { @@ -39,10 +38,8 @@ class VIEWS_EXPORT TooltipManager { virtual ~TooltipManager() {} // Returns the maximum width of the tooltip. |point| gives the location - // the tooltip is to be displayed on in screen coordinates. |context| is - // used to determine which gfx::Screen should be used. - virtual int GetMaxWidth(const gfx::Point& location, - gfx::NativeView context) const = 0; + // the tooltip is to be displayed on in screen coordinates. + virtual int GetMaxWidth(const gfx::Point& location) const = 0; // Returns the font list used for tooltips. virtual const gfx::FontList& GetFontList() const = 0; diff --git a/chromium/ui/views/widget/tooltip_manager_aura.cc b/chromium/ui/views/widget/tooltip_manager_aura.cc index 1c5bd26c32e..c4bf40ea856 100644 --- a/chromium/ui/views/widget/tooltip_manager_aura.cc +++ b/chromium/ui/views/widget/tooltip_manager_aura.cc @@ -49,7 +49,7 @@ void TooltipManagerAura::UpdateTooltipManagerForCapture(Widget* source) { if (!screen_position_client) return; screen_position_client->ConvertPointToScreen(root_window, &screen_loc); - gfx::Screen* screen = gfx::Screen::GetScreenFor(root_window); + gfx::Screen* screen = gfx::Screen::GetScreen(); aura::Window* target = screen->GetWindowAtScreenPoint(screen_loc); if (!target) return; @@ -81,10 +81,10 @@ const gfx::FontList& TooltipManagerAura::GetFontList() const { return GetDefaultFontList(); } -int TooltipManagerAura::GetMaxWidth(const gfx::Point& point, - aura::Window* context) const { - return aura::client::GetTooltipClient(context->GetRootWindow())-> - GetMaxWidth(point, context); +int TooltipManagerAura::GetMaxWidth(const gfx::Point& point) const { + return aura::client::GetTooltipClient( + widget_->GetNativeView()->GetRootWindow()) + ->GetMaxWidth(point); } void TooltipManagerAura::UpdateTooltip() { diff --git a/chromium/ui/views/widget/tooltip_manager_aura.h b/chromium/ui/views/widget/tooltip_manager_aura.h index 8d6bd9aff84..f429385525a 100644 --- a/chromium/ui/views/widget/tooltip_manager_aura.h +++ b/chromium/ui/views/widget/tooltip_manager_aura.h @@ -39,8 +39,7 @@ class TooltipManagerAura : public TooltipManager { static const gfx::FontList& GetDefaultFontList(); // TooltipManager: - int GetMaxWidth(const gfx::Point& location, - aura::Window* context) const override; + int GetMaxWidth(const gfx::Point& location) const override; const gfx::FontList& GetFontList() const override; void UpdateTooltip() override; void TooltipTextChanged(View* view) override; diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 02a570db6da..ffafd0d2bbb 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -10,6 +10,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" #include "ui/base/cursor/cursor.h" +#include "ui/base/default_style.h" #include "ui/base/default_theme_provider.h" #include "ui/base/hit_test.h" #include "ui/base/ime/input_method.h" @@ -110,30 +111,7 @@ class DefaultWidgetDelegate : public WidgetDelegate { //////////////////////////////////////////////////////////////////////////////// // Widget, InitParams: -Widget::InitParams::InitParams() - : type(TYPE_WINDOW), - delegate(nullptr), - child(false), - opacity(INFER_OPACITY), - accept_events(true), - activatable(ACTIVATABLE_DEFAULT), - keep_on_top(false), - visible_on_all_workspaces(false), - ownership(NATIVE_WIDGET_OWNS_WIDGET), - mirror_origin_in_rtl(false), - shadow_type(SHADOW_TYPE_DEFAULT), - remove_standard_frame(false), - use_system_default_icon(false), - show_state(ui::SHOW_STATE_DEFAULT), - parent(nullptr), - native_widget(nullptr), - native_theme(nullptr), - desktop_window_tree_host(nullptr), - layer_type(ui::LAYER_TEXTURED), - context(nullptr), - force_show_in_taskbar(false), - force_software_compositing(false) { -} +Widget::InitParams::InitParams() : InitParams(TYPE_WINDOW) {} Widget::InitParams::InitParams(Type type) : type(type), @@ -152,7 +130,6 @@ Widget::InitParams::InitParams(Type type) show_state(ui::SHOW_STATE_DEFAULT), parent(nullptr), native_widget(nullptr), - native_theme(nullptr), desktop_window_tree_host(nullptr), layer_type(ui::LAYER_TEXTURED), context(nullptr), @@ -160,6 +137,8 @@ Widget::InitParams::InitParams(Type type) force_software_compositing(false) { } +Widget::InitParams::InitParams(const InitParams& other) = default; + Widget::InitParams::~InitParams() { } @@ -168,14 +147,13 @@ Widget::InitParams::~InitParams() { Widget::Widget() : native_widget_(nullptr), - native_theme_(nullptr), widget_delegate_(nullptr), non_client_view_(nullptr), dragged_view_(nullptr), ownership_(InitParams::NATIVE_WIDGET_OWNS_WIDGET), is_secondary_widget_(true), frame_type_(FRAME_TYPE_DEFAULT), - disable_inactive_rendering_(false), + always_render_as_active_(false), widget_closed_(false), saved_show_state_(ui::SHOW_STATE_DEFAULT), focus_on_creation_(true), @@ -203,22 +181,6 @@ Widget::~Widget() { } // static -Widget* Widget::CreateWindow(WidgetDelegate* delegate) { - return CreateWindowWithBounds(delegate, gfx::Rect()); -} - -// static -Widget* Widget::CreateWindowWithBounds(WidgetDelegate* delegate, - const gfx::Rect& bounds) { - Widget* widget = new Widget; - Widget::InitParams params; - params.bounds = bounds; - params.delegate = delegate; - widget->Init(params); - return widget; -} - -// static Widget* Widget::CreateWindowWithParent(WidgetDelegate* delegate, gfx::NativeView parent) { return CreateWindowWithParentAndBounds(delegate, parent, gfx::Rect()); @@ -278,7 +240,6 @@ Widget* Widget::GetTopLevelWidgetForNativeView(gfx::NativeView native_view) { return native_widget ? native_widget->GetWidget() : NULL; } - // static void Widget::GetAllChildWidgets(gfx::NativeView native_view, Widgets* children) { @@ -299,14 +260,16 @@ void Widget::ReparentNativeView(gfx::NativeView native_view, // static int Widget::GetLocalizedContentsWidth(int col_resource_id) { - return ui::GetLocalizedContentsWidthForFont(col_resource_id, - ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); + return ui::GetLocalizedContentsWidthForFont( + col_resource_id, ResourceBundle::GetSharedInstance().GetFontWithDelta( + ui::kMessageFontSizeDelta)); } // static int Widget::GetLocalizedContentsHeight(int row_resource_id) { - return ui::GetLocalizedContentsHeightForFont(row_resource_id, - ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont)); + return ui::GetLocalizedContentsHeightForFont( + row_resource_id, ResourceBundle::GetSharedInstance().GetFontWithDelta( + ui::kMessageFontSizeDelta)); } // static @@ -369,7 +332,6 @@ void Widget::Init(const InitParams& in_params) { internal::NativeWidgetPrivate::IsMouseButtonDown(); } native_widget_->InitNativeWidget(params); - native_theme_ = params.native_theme; if (RequiresNonClientView(params.type)) { non_client_view_ = new NonClientView; non_client_view_->SetFrameView(CreateNonClientFrameView()); @@ -532,9 +494,9 @@ void Widget::CenterWindow(const gfx::Size& size) { } void Widget::SetBoundsConstrained(const gfx::Rect& bounds) { - gfx::Rect work_area = - gfx::Screen::GetScreenFor(GetNativeView())->GetDisplayNearestPoint( - bounds.origin()).work_area(); + gfx::Rect work_area = gfx::Screen::GetScreen() + ->GetDisplayNearestPoint(bounds.origin()) + .work_area(); if (work_area.IsEmpty()) { SetBounds(bounds); } else { @@ -683,10 +645,6 @@ bool Widget::IsActive() const { return native_widget_->IsActive(); } -void Widget::DisableInactiveRendering() { - SetInactiveRenderingDisabled(true); -} - void Widget::SetAlwaysOnTop(bool on_top) { native_widget_->SetAlwaysOnTop(on_top); } @@ -737,10 +695,6 @@ void Widget::SetOpacity(unsigned char opacity) { native_widget_->SetOpacity(opacity); } -void Widget::SetUseDragFrame(bool use_drag_frame) { - native_widget_->SetUseDragFrame(use_drag_frame); -} - void Widget::FlashFrame(bool flash) { native_widget_->FlashFrame(flash); } @@ -774,7 +728,7 @@ const ui::ThemeProvider* Widget::GetThemeProvider() const { } const ui::NativeTheme* Widget::GetNativeTheme() const { - return native_theme_? native_theme_ : native_widget_->GetNativeTheme(); + return native_widget_->GetNativeTheme(); } FocusManager* Widget::GetFocusManager() { @@ -1007,7 +961,7 @@ void Widget::SynthesizeMouseMoveEvent() { // In screen coordinate. gfx::Point mouse_location = EventMonitor::GetLastMouseLocation(); if (!GetWindowBoundsInScreen().Contains(mouse_location)) - return; + return; // Convert: screen coordinate -> widget coordinate. View::ConvertPointFromScreen(root_view_.get(), &mouse_location); @@ -1031,8 +985,7 @@ void Widget::OnSizeConstraintsChanged() { non_client_view_->SizeConstraintsChanged(); } -void Widget::OnOwnerClosing() { -} +void Widget::OnOwnerClosing() {} //////////////////////////////////////////////////////////////////////////////// // Widget, NativeWidgetDelegate implementation: @@ -1049,12 +1002,8 @@ bool Widget::CanActivate() const { return widget_delegate_->CanActivate(); } -bool Widget::IsInactiveRenderingDisabled() const { - return disable_inactive_rendering_; -} - -void Widget::EnableInactiveRendering() { - SetInactiveRenderingDisabled(false); +bool Widget::IsAlwaysRenderAsActive() const { + return always_render_as_active_; } void Widget::OnNativeWidgetActivationChanged(bool active) { @@ -1067,8 +1016,8 @@ void Widget::OnNativeWidgetActivationChanged(bool active) { FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetActivationChanged(this, active)); - if (IsVisible() && non_client_view()) - non_client_view()->frame_view()->SchedulePaint(); + if (non_client_view()) + non_client_view()->frame_view()->ActivationChanged(active); } void Widget::OnNativeFocus() { @@ -1338,7 +1287,7 @@ bool Widget::SetInitialFocus(ui::WindowShowState show_state) { show_state == ui::SHOW_STATE_MINIMIZED) { // If not focusing the window now, tell the focus manager which view to // focus when the window is restored. - if (v) + if (v && focus_manager_.get()) focus_manager_->SetStoredFocusView(v); return true; } @@ -1401,22 +1350,24 @@ void Widget::DestroyRootView() { root_view_.reset(); } -void Widget::OnDragWillStart() { -} +void Widget::OnDragWillStart() {} -void Widget::OnDragComplete() { -} +void Widget::OnDragComplete() {} //////////////////////////////////////////////////////////////////////////////// // Widget, private: -void Widget::SetInactiveRenderingDisabled(bool value) { - if (value == disable_inactive_rendering_) +void Widget::SetAlwaysRenderAsActive(bool always_render_as_active) { + if (always_render_as_active_ == always_render_as_active) return; - disable_inactive_rendering_ = value; - if (non_client_view_) - non_client_view_->SetInactiveRenderingDisabled(value); + always_render_as_active_ = always_render_as_active; + + // If active, the frame should already be painted. Otherwise, + // |always_render_as_active_| just changed, and the widget is inactive, so + // schedule a repaint. + if (non_client_view_ && !IsActive()) + non_client_view_->frame_view()->SchedulePaint(); } void Widget::SaveWindowPlacement() { diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index 80aaddee1a6..0ceaf825ffb 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -97,8 +97,8 @@ class RootView; // The Widget instance owns its NativeWidget. This state implies someone // else wants to control the lifetime of this object. When they destroy // the Widget it is responsible for destroying the NativeWidget (from its -// destructor). -// +// destructor). This is often used to place a Widget in a scoped_ptr<> or +// on the stack in a test. class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, public ui::EventSource, public FocusTraversable, @@ -202,25 +202,27 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, InitParams(); explicit InitParams(Type type); + InitParams(const InitParams& other); ~InitParams(); Type type; - // If NULL, a default implementation will be constructed. + // If null, a default implementation will be constructed. The default + // implementation deletes itself when the Widget closes. WidgetDelegate* delegate; bool child; // If TRANSLUCENT_WINDOW, the widget may be fully or partially transparent. - // Translucent windows may not always be supported. Use - // IsTranslucentWindowOpacitySupported to determine if translucent windows - // are supported. // If OPAQUE_WINDOW, we can perform optimizations based on the widget being - // fully opaque. Defaults to TRANSLUCENT_WINDOW if - // ViewsDelegate::UseTransparentWindows(). Defaults to OPAQUE_WINDOW for - // non-window widgets. + // fully opaque. + // Default is based on ViewsDelegate::GetOpacityForInitParams(). Defaults + // to OPAQUE_WINDOW for non-window widgets. + // Translucent windows may not always be supported. Use + // IsTranslucentWindowOpacitySupported to determine whether they are. WindowOpacity opacity; bool accept_events; Activatable activatable; bool keep_on_top; bool visible_on_all_workspaces; + // See Widget class comment above. Ownership ownership; bool mirror_origin_in_rtl; ShadowType shadow_type; @@ -242,8 +244,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // When set, this value is used as the Widget's NativeWidget implementation. // The Widget will not construct a default one. Default is NULL. NativeWidget* native_widget; - // If provided, sets the native theme for this widget. - ui::NativeTheme* native_theme; // Aura-only. Provides a DesktopWindowTreeHost implementation to use instead // of the default one. // TODO(beng): Figure out if there's a better way to expose this, e.g. get @@ -273,23 +273,18 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // If true then the widget uses software compositing. Defaults to false. // Only used on Windows. bool force_software_compositing; + + // Used if widget is not activatable to do determine if mouse events should + // be sent to the widget. + bool wants_mouse_events_when_inactive = false; }; Widget(); ~Widget() override; - // Creates a toplevel window with no context. These methods should only be - // used in cases where there is no contextual information because we're - // creating a toplevel window connected to no other event. - // - // If you have any parenting or context information, or can pass that - // information, prefer the WithParent or WithContext versions of these - // methods. - static Widget* CreateWindow(WidgetDelegate* delegate); - static Widget* CreateWindowWithBounds(WidgetDelegate* delegate, - const gfx::Rect& bounds); - - // Creates a decorated window Widget with the specified properties. + // Creates a decorated window Widget with the specified properties. The + // returned Widget is owned by its NativeWidget; see Widget class comment for + // details. static Widget* CreateWindowWithParent(WidgetDelegate* delegate, gfx::NativeView parent); static Widget* CreateWindowWithParentAndBounds(WidgetDelegate* delegate, @@ -297,6 +292,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, const gfx::Rect& bounds); // Creates a decorated window Widget in the same desktop context as |context|. + // The returned Widget is owned by its NativeWidget; see Widget class comment + // for details. static Widget* CreateWindowWithContext(WidgetDelegate* delegate, gfx::NativeWindow context); static Widget* CreateWindowWithContextAndBounds(WidgetDelegate* delegate, @@ -498,12 +495,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Returns whether the Widget is the currently active window. virtual bool IsActive() const; - // Prevents the window from being rendered as deactivated. This state is - // reset automatically as soon as the window becomes activated again. There is - // no ability to control the state through this API as this leads to sync - // problems. - void DisableInactiveRendering(); - // Sets the widget to be on top of all other widgets in the windowing system. void SetAlwaysOnTop(bool on_top); @@ -532,10 +523,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // underlying windowing system. void SetOpacity(unsigned char opacity); - // Sets whether or not the window should show its frame as a "transient drag - // frame" - slightly transparent and without the standard window controls. - void SetUseDragFrame(bool use_drag_frame); - // Flashes the frame of the window to draw attention to it. Currently only // implemented on Windows for non-Aura. void FlashFrame(bool flash); @@ -565,7 +552,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, return const_cast<ui::NativeTheme*>( const_cast<const Widget*>(this)->GetNativeTheme()); } - const ui::NativeTheme* GetNativeTheme() const; + virtual const ui::NativeTheme* GetNativeTheme() const; // Returns the FocusManager for this widget. // Note that all widgets in a widget hierarchy share the same focus manager. @@ -775,8 +762,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, bool IsModal() const override; bool IsDialogBox() const override; bool CanActivate() const override; - bool IsInactiveRenderingDisabled() const override; - void EnableInactiveRendering() override; + bool IsAlwaysRenderAsActive() const override; + void SetAlwaysRenderAsActive(bool always_render_as_active) override; void OnNativeWidgetActivationChanged(bool active) override; void OnNativeFocus() override; void OnNativeBlur() override; @@ -838,12 +825,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, private: friend class ComboboxTest; + friend class CustomButtonTest; friend class TextfieldTest; - // Sets the value of |disable_inactive_rendering_|. If the value changes, - // both the NonClientView and WidgetDelegate are notified. - void SetInactiveRenderingDisabled(bool value); - // Persists the window's restored position and "show" state using the // window delegate. void SaveWindowPlacement(); @@ -866,10 +850,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, internal::NativeWidgetPrivate* native_widget_; - // If non-null, the native theme for this widget. Otherwise the native theme - // comes from |native_widget_|. - ui::NativeTheme* native_theme_; - base::ObserverList<WidgetObserver> observers_; base::ObserverList<WidgetRemovalsObserver> removals_observers_; @@ -914,7 +894,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // True when the window should be rendered as active, regardless of whether // or not it actually is. - bool disable_inactive_rendering_; + bool always_render_as_active_; // Set to true if the widget is in the process of closing. bool widget_closed_; diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc index e62aa92847e..ced90b6f392 100644 --- a/chromium/ui/views/widget/widget_delegate.cc +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -32,15 +32,19 @@ void WidgetDelegate::OnWorkAreaChanged() { } View* WidgetDelegate::GetInitiallyFocusedView() { - return NULL; + return nullptr; } BubbleDelegateView* WidgetDelegate::AsBubbleDelegate() { - return NULL; + return nullptr; +} + +BubbleDialogDelegateView* WidgetDelegate::AsBubbleDialogDelegate() { + return nullptr; } DialogDelegate* WidgetDelegate::AsDialogDelegate() { - return NULL; + return nullptr; } bool WidgetDelegate::CanResize() const { @@ -80,7 +84,11 @@ bool WidgetDelegate::ShouldShowWindowTitle() const { } bool WidgetDelegate::ShouldShowCloseButton() const { +#if defined(OS_MACOSX) + return false; +#else return true; +#endif } bool WidgetDelegate::ShouldHandleSystemCommands() const { diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h index 660313d1648..0ec021505b6 100644 --- a/chromium/ui/views/widget/widget_delegate.h +++ b/chromium/ui/views/widget/widget_delegate.h @@ -19,6 +19,7 @@ class Rect; } namespace views { +class BubbleDialogDelegateView; class BubbleDelegateView; class ClientView; class DialogDelegate; @@ -51,6 +52,7 @@ class VIEWS_EXPORT WidgetDelegate { virtual View* GetInitiallyFocusedView(); virtual BubbleDelegateView* AsBubbleDelegate(); + virtual BubbleDialogDelegateView* AsBubbleDialogDelegate(); virtual DialogDelegate* AsDialogDelegate(); // Returns true if the window can be resized. diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index fa5fb3954fc..e9f9397c01b 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -26,6 +26,7 @@ #include "ui/views/controls/textfield/textfield_test_api.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/test/focus_manager_test.h" +#include "ui/views/test/native_widget_factory.h" #include "ui/views/test/widget_test.h" #include "ui/views/touchui/touch_selection_controller_impl.h" #include "ui/views/widget/widget.h" @@ -52,7 +53,7 @@ class ExitLoopOnRelease : public View { ~ExitLoopOnRelease() override {} private: - // Overridden from View: + // View: void OnMouseReleased(const ui::MouseEvent& event) override { GetWidget()->Close(); base::MessageLoop::current()->QuitNow(); @@ -68,7 +69,7 @@ class GestureCaptureView : public View { ~GestureCaptureView() override {} private: - // Overridden from View: + // View: void OnGestureEvent(ui::GestureEvent* event) override { if (event->type() == ui::ET_GESTURE_TAP_DOWN) { GetWidget()->SetCapture(this); @@ -132,7 +133,7 @@ class NestedLoopCaptureView : public View { ~NestedLoopCaptureView() override {} private: - // Overridden from View: + // View: bool OnMousePressed(const ui::MouseEvent& event) override { // Start a nested loop. widget_->Show(); @@ -654,6 +655,7 @@ TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { } widget->Show(); + widget->GetNativeWindow()->GetHost()->Show(); const HWND hwnd = HWNDForWidget(widget); EXPECT_TRUE(::IsWindow(hwnd)); EXPECT_TRUE(::IsWindowEnabled(hwnd)); @@ -740,6 +742,69 @@ TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) { EXPECT_EQ(true, widget1.active()); EXPECT_EQ(false, widget2.active()); } + +// On Windows if we create a fullscreen window on a thread, then it affects the +// way other windows on the thread interact with the taskbar. To workaround +// this we reduce the bounds of a fullscreen window by 1px when it loses +// activation. This test verifies the same. +TEST_F(WidgetTestInteractive, FullscreenBoundsReducedOnActivationLoss) { + Widget widget1; + Widget::InitParams params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + params.native_widget = new DesktopNativeWidgetAura(&widget1); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget1.Init(params); + widget1.SetBounds(gfx::Rect(0, 0, 200, 200)); + widget1.Show(); + + widget1.Activate(); + RunPendingMessages(); + EXPECT_EQ(::GetActiveWindow(), + widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + + widget1.SetFullscreen(true); + EXPECT_TRUE(widget1.IsFullscreen()); + // Ensure that the StopIgnoringPosChanges task in HWNDMessageHandler runs. + // This task is queued when a widget becomes fullscreen. + RunPendingMessages(); + EXPECT_EQ(::GetActiveWindow(), + widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + gfx::Rect fullscreen_bounds = widget1.GetWindowBoundsInScreen(); + + Widget widget2; + params.native_widget = new DesktopNativeWidgetAura(&widget2); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget2.Init(params); + widget2.SetBounds(gfx::Rect(0, 0, 200, 200)); + widget2.Show(); + + widget2.Activate(); + RunPendingMessages(); + EXPECT_EQ(::GetActiveWindow(), + widget2.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + + gfx::Rect fullscreen_bounds_after_activation_loss = + widget1.GetWindowBoundsInScreen(); + + // After deactivation loss the bounds of the fullscreen widget should be + // reduced by 1px. + EXPECT_EQ(fullscreen_bounds.height() - + fullscreen_bounds_after_activation_loss.height(), 1); + + widget1.Activate(); + RunPendingMessages(); + EXPECT_EQ(::GetActiveWindow(), + widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + + gfx::Rect fullscreen_bounds_after_activate = + widget1.GetWindowBoundsInScreen(); + + // After activation the bounds of the fullscreen widget should be restored. + EXPECT_EQ(fullscreen_bounds, fullscreen_bounds_after_activate); + + widget1.CloseNow(); + widget2.CloseNow(); +} #endif // defined(OS_WIN) #if !defined(OS_CHROMEOS) @@ -774,8 +839,8 @@ TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) { gfx::Rect initial_bounds(0, 0, 500, 500); init_params.bounds = initial_bounds; init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = - new PlatformDesktopNativeWidget(&top_level_widget); + init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl( + init_params, &top_level_widget, nullptr); top_level_widget.Init(init_params); ShowSync(&top_level_widget); @@ -843,8 +908,8 @@ TEST_F(WidgetTestInteractive, MAYBE_SystemModalWindowReleasesCapture) { gfx::Rect initial_bounds(0, 0, 500, 500); init_params.bounds = initial_bounds; init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = - new PlatformDesktopNativeWidget(&top_level_widget); + init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl( + init_params, &top_level_widget, nullptr); top_level_widget.Init(init_params); ShowSync(&top_level_widget); @@ -881,9 +946,8 @@ TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) { init_params.bounds = gfx::Rect(0, 0, 200, 200); init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; init_params.activatable = Widget::InitParams::ACTIVATABLE_NO; -#if !defined(OS_CHROMEOS) - init_params.native_widget = new PlatformDesktopNativeWidget(&widget); -#endif // !defined(OS_CHROMEOS) + init_params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(init_params, &widget, nullptr); widget.Init(init_params); widget.Show(); @@ -1048,7 +1112,8 @@ TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) { Widget widget2; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = new PlatformDesktopNativeWidget(&widget2); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, &widget2, nullptr); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget2.Init(params); widget2.Show(); @@ -1107,9 +1172,35 @@ TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) { RunPendingMessages(); } +// Testing initial focus is assigned properly for normal top-level widgets, +// and subclasses that specify a initially focused child view. +TEST_F(WidgetTestInteractive, InitialFocus) { + // By default, there is no initially focused view (even if there is a + // focusable subview). + Widget* toplevel(CreateTopLevelPlatformWidget()); + View* view = new View; + view->SetFocusable(true); + toplevel->GetContentsView()->AddChildView(view); + + ShowSync(toplevel); + toplevel->Show(); + EXPECT_FALSE(view->HasFocus()); + EXPECT_FALSE(toplevel->GetFocusManager()->GetStoredFocusView()); + toplevel->CloseNow(); + + // Testing a widget which specifies a initially focused view. + TestInitialFocusWidgetDelegate delegate(GetContext()); + + Widget* widget = delegate.GetWidget(); + ShowSync(widget); + widget->Show(); + EXPECT_TRUE(delegate.view()->HasFocus()); + EXPECT_EQ(delegate.view(), widget->GetFocusManager()->GetStoredFocusView()); +} + namespace { -// Used to veirfy OnMouseCaptureLost() has been invoked. +// Used to verify OnMouseCaptureLost() has been invoked. class CaptureLostTrackingWidget : public Widget { public: CaptureLostTrackingWidget() : got_capture_lost_(false) {} @@ -1157,8 +1248,8 @@ class WidgetCaptureTest : public ViewsTestBase { CaptureLostTrackingWidget widget1; Widget::InitParams params1 = CreateParams(views::Widget::InitParams::TYPE_WINDOW); - params1.native_widget = CreateNativeWidget(use_desktop_native_widget, - &widget1); + params1.native_widget = + CreateNativeWidget(params1, use_desktop_native_widget, &widget1); params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget1.Init(params1); widget1.Show(); @@ -1167,8 +1258,8 @@ class WidgetCaptureTest : public ViewsTestBase { Widget::InitParams params2 = CreateParams(views::Widget::InitParams::TYPE_WINDOW); params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params2.native_widget = CreateNativeWidget(use_desktop_native_widget, - &widget2); + params2.native_widget = + CreateNativeWidget(params2, use_desktop_native_widget, &widget2); widget2.Init(params2); widget2.Show(); @@ -1194,13 +1285,12 @@ class WidgetCaptureTest : public ViewsTestBase { EXPECT_FALSE(widget2.GetAndClearGotCaptureLost()); } - NativeWidget* CreateNativeWidget(bool create_desktop_native_widget, + NativeWidget* CreateNativeWidget(const Widget::InitParams& params, + bool create_desktop_native_widget, Widget* widget) { -#if !defined(OS_CHROMEOS) if (create_desktop_native_widget) - return new PlatformDesktopNativeWidget(widget); -#endif - return NULL; + return CreatePlatformDesktopNativeWidgetImpl(params, widget, nullptr); + return CreatePlatformNativeWidgetImpl(params, widget, kDefault, nullptr); } private: @@ -1266,7 +1356,7 @@ TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) { Widget widget1; Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params1.native_widget = CreateNativeWidget(true, &widget1); + params1.native_widget = CreateNativeWidget(params1, true, &widget1); params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget1.Init(params1); MouseView* mouse_view1 = new MouseView; @@ -1277,7 +1367,7 @@ TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) { Widget widget2; Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params2.native_widget = CreateNativeWidget(true, &widget2); + params2.native_widget = CreateNativeWidget(params2, true, &widget2); params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget2.Init(params2); widget2.Show(); @@ -1329,7 +1419,8 @@ TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) { Widget toplevel; Widget::InitParams toplevel_params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); - toplevel_params.native_widget = CreateNativeWidget(true, &toplevel); + toplevel_params.native_widget = CreateNativeWidget(toplevel_params, true, + &toplevel); toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; toplevel.Init(toplevel_params); toplevel.Show(); diff --git a/chromium/ui/views/widget/widget_removals_observer.h b/chromium/ui/views/widget/widget_removals_observer.h index e62acab36a4..d7046073c4d 100644 --- a/chromium/ui/views/widget/widget_removals_observer.h +++ b/chromium/ui/views/widget/widget_removals_observer.h @@ -18,7 +18,11 @@ class View; // from |View|. class VIEWS_EXPORT WidgetRemovalsObserver { public: - // Called immediately before a descendant view of |widget| is removed. + // Called immediately before a descendant view of |widget| is removed + // from this widget. Won't be called if the view is moved within the + // same widget, but will be called if it's moved to a different widget. + // Only called on the root of a view tree; it implies that all of the + // descendants of |view| will be removed. virtual void OnWillRemoveView(Widget* widget, View* view) {} protected: diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index 41bbe8198d7..0b8c645a5ea 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -23,12 +23,15 @@ #include "ui/gfx/native_widget_types.h" #include "ui/views/bubble/bubble_delegate.h" #include "ui/views/controls/textfield/textfield.h" +#include "ui/views/test/native_widget_factory.h" #include "ui/views/test/test_views.h" #include "ui/views/test/test_widget_observer.h" #include "ui/views/test/widget_test.h" #include "ui/views/widget/native_widget_delegate.h" +#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget_deletion_observer.h" +#include "ui/views/widget/widget_removals_observer.h" #include "ui/views/window/dialog_delegate.h" #include "ui/views/window/native_frame_view.h" @@ -42,7 +45,6 @@ #if defined(OS_MACOSX) #include "base/mac/mac_util.h" -#include "ui/base/test/scoped_fake_nswindow_fullscreen.h" #endif namespace views { @@ -59,15 +61,6 @@ gfx::Point ConvertPointFromWidgetToView(View* view, const gfx::Point& p) { return tmp; } -// Helper function for Snow Leopard special cases to avoid #ifdef litter. -bool IsTestingSnowLeopard() { -#if defined(OS_MACOSX) - return base::mac::IsOSSnowLeopard(); -#else - return false; -#endif -} - // This class can be used as a deleter for scoped_ptr<Widget> // to call function Widget::CloseNow automatically. struct WidgetCloser { @@ -367,44 +360,6 @@ struct OwnershipTestState { bool native_widget_deleted; }; -// A platform NativeWidget subclass that updates a bag of state when it is -// destroyed. -class OwnershipTestNativeWidget : public PlatformNativeWidget { - public: - OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate, - OwnershipTestState* state) - : PlatformNativeWidget(delegate), - state_(state) { - } - ~OwnershipTestNativeWidget() override { - state_->native_widget_deleted = true; - } - - private: - OwnershipTestState* state_; - - DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidget); -}; - -// A views NativeWidget subclass that updates a bag of state when it is -// destroyed. -class OwnershipTestNativeWidgetAura : public NativeWidgetCapture { - public: - OwnershipTestNativeWidgetAura(internal::NativeWidgetDelegate* delegate, - OwnershipTestState* state) - : NativeWidgetCapture(delegate), - state_(state) { - } - ~OwnershipTestNativeWidgetAura() override { - state_->native_widget_deleted = true; - } - - private: - OwnershipTestState* state_; - - DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetAura); -}; - // A Widget subclass that updates a bag of state when it is destroyed. class OwnershipTestWidget : public Widget { public: @@ -417,6 +372,8 @@ class OwnershipTestWidget : public Widget { DISALLOW_COPY_AND_ASSIGN(OwnershipTestWidget); }; +// TODO(sky): add coverage of ownership for the desktop variants. + // Widget owns its NativeWidget, part 1: NativeWidget is a platform-native // widget. TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { @@ -424,8 +381,8 @@ TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget.get(), &state); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget.get(), kStubCapture, &state.native_widget_deleted); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(params); @@ -445,8 +402,8 @@ TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) { scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget.get(), &state); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget.get(), kStubCapture, &state.native_widget_deleted); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(params); @@ -470,9 +427,9 @@ TEST_F(WidgetOwnershipTest, scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget.get(), &state); params.parent = toplevel->GetNativeView(); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget.get(), kStubCapture, &state.native_widget_deleted); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(params); @@ -500,8 +457,8 @@ TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget, &state); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget, kStubCapture, &state.native_widget_deleted); widget->Init(params); // Now destroy the native widget. @@ -519,9 +476,9 @@ TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) { Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget, &state); params.parent = toplevel->GetNativeView(); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget, kStubCapture, &state.native_widget_deleted); widget->Init(params); // Now destroy the native widget. This is achieved by closing the toplevel. @@ -543,8 +500,8 @@ TEST_F(WidgetOwnershipTest, Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget, &state); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget, kStubCapture, &state.native_widget_deleted); widget->Init(params); // Now simulate a destroy of the platform native widget from the OS: @@ -564,9 +521,9 @@ TEST_F(WidgetOwnershipTest, Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget, &state); params.parent = toplevel->GetNativeView(); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget, kStubCapture, &state.native_widget_deleted); widget->Init(params); // Destroy the widget (achieved by closing the toplevel). @@ -590,9 +547,9 @@ TEST_F(WidgetOwnershipTest, Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget, &state); params.parent = toplevel->GetNativeView(); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget, kStubCapture, &state.native_widget_deleted); widget->Init(params); // Destroy the widget. @@ -616,8 +573,8 @@ TEST_F(WidgetOwnershipTest, scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = - new OwnershipTestNativeWidgetAura(widget.get(), &state); + params.native_widget = CreatePlatformNativeWidgetImpl( + params, widget.get(), kStubCapture, &state.native_widget_deleted); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.delegate = delegate_view; widget->Init(params); @@ -658,7 +615,6 @@ class WidgetWithDestroyedNativeViewTest : public ViewsTestBase { widget->Activate(); widget->Deactivate(); widget->IsActive(); - widget->DisableInactiveRendering(); widget->SetAlwaysOnTop(true); widget->IsAlwaysOnTop(); widget->Maximize(); @@ -667,7 +623,6 @@ class WidgetWithDestroyedNativeViewTest : public ViewsTestBase { widget->IsMaximized(); widget->IsFullscreen(); widget->SetOpacity(0); - widget->SetUseDragFrame(true); widget->FlashFrame(true); widget->IsVisible(); widget->GetThemeProvider(); @@ -706,7 +661,8 @@ TEST_F(WidgetWithDestroyedNativeViewTest, Test) { { Widget widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = new PlatformDesktopNativeWidget(&widget); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, &widget, nullptr); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget.Init(params); widget.Show(); @@ -971,6 +927,11 @@ TEST_F(WidgetObserverTest, ClosingOnHiddenParent) { // Test behavior of NativeWidget*::GetWindowPlacement on the native desktop. TEST_F(WidgetTest, GetWindowPlacement) { + if (IsMus()) { + NOTIMPLEMENTED(); + return; + } + WidgetAutoclosePtr widget; #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // On desktop-Linux cheat and use non-desktop widgets. On X11, minimize is @@ -1081,10 +1042,12 @@ TEST_F(WidgetTest, MinimumSizeConstraints) { widget->SetSize(smaller_size); #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // TODO(tapted): Desktop Linux ignores size constraints for SetSize. Fix it. - EXPECT_EQ(smaller_size, widget->GetClientAreaBoundsInScreen().size()); + const bool use_small_size = IsMus() ? false : true; #else - EXPECT_EQ(minimum_size, widget->GetClientAreaBoundsInScreen().size()); + const bool use_small_size = false; #endif + EXPECT_EQ(use_small_size ? smaller_size : minimum_size, + widget->GetClientAreaBoundsInScreen().size()); } // Tests that SetBounds() and GetWindowBoundsInScreen() is symmetric when the @@ -1155,10 +1118,10 @@ TEST_F(WidgetTest, GetWindowBoundsInScreen) { // Test that GetRestoredBounds() returns the original bounds of the window. TEST_F(WidgetTest, MAYBE_GetRestoredBounds) { -#if defined(OS_MACOSX) - // Fullscreen on Mac requires an interactive UI test, fake them here. - ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen; -#endif + if (IsMus()) { + NOTIMPLEMENTED(); + return; + } WidgetAutoclosePtr toplevel(CreateNativeDesktopWidget()); toplevel->Show(); @@ -1188,14 +1151,8 @@ TEST_F(WidgetTest, MAYBE_GetRestoredBounds) { toplevel->SetFullscreen(true); RunPendingMessages(); - if (IsTestingSnowLeopard()) { - // Fullscreen not implemented for Snow Leopard. - EXPECT_EQ(toplevel->GetWindowBoundsInScreen(), - toplevel->GetRestoredBounds()); - } else { - EXPECT_NE(toplevel->GetWindowBoundsInScreen(), - toplevel->GetRestoredBounds()); - } + EXPECT_NE(toplevel->GetWindowBoundsInScreen(), + toplevel->GetRestoredBounds()); EXPECT_EQ(bounds, toplevel->GetRestoredBounds()); toplevel->SetFullscreen(false); @@ -1239,7 +1196,8 @@ TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) { init_params.bounds = gfx::Rect(0, 0, 200, 200); init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; #if !defined(OS_CHROMEOS) - init_params.native_widget = new PlatformDesktopNativeWidget(&widget); + init_params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(init_params, &widget, nullptr); #endif widget.Init(init_params); widget.SetContentsView(contents_view); @@ -1301,6 +1259,10 @@ TEST_F(WidgetTest, BubbleControlsResetOnInit) { // the case for desktop widgets on Windows. Other platforms retain the window // size while minimized. TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) { + if (IsMus()) { + // This test is testing behavior specific to DesktopWindowTreeHostWin. + return; + } // Create a widget. Widget widget; Widget::InitParams init_params = @@ -1309,7 +1271,8 @@ TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) { gfx::Rect initial_bounds(0, 0, 300, 400); init_params.bounds = initial_bounds; init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = new PlatformDesktopNativeWidget(&widget); + init_params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(init_params, &widget, nullptr); widget.Init(init_params); NonClientView* non_client_view = widget.non_client_view(); NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); @@ -1383,7 +1346,8 @@ class DesktopAuraTestValidPaintWidget : public views::Widget { void DesktopAuraTestValidPaintWidget::InitForTest(InitParams init_params) { init_params.bounds = gfx::Rect(0, 0, 200, 200); init_params.ownership = InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = new PlatformDesktopNativeWidget(this); + init_params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(init_params, this, nullptr); Init(init_params); View* contents_view = new View; @@ -1394,7 +1358,14 @@ void DesktopAuraTestValidPaintWidget::InitForTest(InitParams init_params) { Activate(); } -TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterCloseTest) { +#if defined(OS_LINUX) +// Flaky on Linux rel ng: https://crbug.com/596039. +#define MAYBE_DesktopNativeWidgetNoPaintAfterCloseTest DISABLED_DesktopNativeWidgetNoPaintAfterCloseTest +#else +#define MAYBE_DesktopNativeWidgetNoPaintAfterCloseTest DesktopNativeWidgetNoPaintAfterCloseTest +#endif + +TEST_F(WidgetTest, MAYBE_DesktopNativeWidgetNoPaintAfterCloseTest) { DesktopAuraTestValidPaintWidget widget; widget.InitForTest(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); RunPendingMessages(); @@ -1406,7 +1377,12 @@ TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterCloseTest) { EXPECT_FALSE(widget.received_paint_while_hidden()); } -TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterHideTest) { +#if defined(OS_LINUX) +#define MAYBE_DesktopNativeWidgetNoPaintAfterHideTest DISABLED_DesktopNativeWidgetNoPaintAfterHideTest +#else +#define MAYBE_DesktopNativeWidgetNoPaintAfterHideTest DesktopNativeWidgetNoPaintAfterHideTest +#endif +TEST_F(WidgetTest, MAYBE_DesktopNativeWidgetNoPaintAfterHideTest) { DesktopAuraTestValidPaintWidget widget; widget.InitForTest(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); RunPendingMessages(); @@ -1430,7 +1406,8 @@ TEST_F(WidgetTest, TestWindowVisibilityAfterHide) { gfx::Rect initial_bounds(0, 0, 300, 400); init_params.bounds = initial_bounds; init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = new PlatformDesktopNativeWidget(&widget); + init_params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(init_params, &widget, nullptr); widget.Init(init_params); NonClientView* non_client_view = widget.non_client_view(); NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); @@ -1690,7 +1667,9 @@ TEST_F(WidgetTest, SynthesizeMouseMoveEvent) { EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED)); gfx::Point cursor_location(5, 5); - ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); + ui::test::EventGenerator generator( + IsMus() ? widget->GetNativeWindow() : GetContext(), + widget->GetNativeWindow()); generator.MoveMouseTo(cursor_location); EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_MOVED)); @@ -1734,7 +1713,7 @@ class MousePressEventConsumer : public ui::EventHandler { // Test that mouse presses and mouse releases are dispatched normally when a // touch is down. TEST_F(WidgetTest, MouseEventDispatchWhileTouchIsDown) { - WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); + Widget* widget = CreateTopLevelNativeWidget(); widget->Show(); widget->SetSize(gfx::Size(300, 300)); @@ -1745,12 +1724,17 @@ TEST_F(WidgetTest, MouseEventDispatchWhileTouchIsDown) { MousePressEventConsumer consumer; event_count_view->AddPostTargetHandler(&consumer); - ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); - generator.PressTouch(); - generator.ClickLeftButton(); + scoped_ptr<ui::test::EventGenerator> generator(new ui::test::EventGenerator( + IsMus() ? widget->GetNativeWindow() : GetContext(), + widget->GetNativeWindow())); + generator->PressTouch(); + generator->ClickLeftButton(); EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_PRESSED)); EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_RELEASED)); + + // For mus it's important we destroy the widget before the EventGenerator. + widget->CloseNow(); } #endif // !defined(OS_MACOSX) || defined(USE_AURA) @@ -1771,15 +1755,16 @@ class WidgetWindowTitleTest : public WidgetTest { WidgetAutoclosePtr widget(new Widget()); // Destroyed by CloseNow(). Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW); - widget->Init(init_params); #if !defined(OS_CHROMEOS) if (desktop_native_widget) - init_params.native_widget = new PlatformDesktopNativeWidget(widget.get()); + init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl( + init_params, widget.get(), nullptr); #else DCHECK(!desktop_native_widget) << "DesktopNativeWidget does not exist on non-Aura or on ChromeOS."; #endif + widget->Init(init_params); internal::NativeWidgetPrivate* native_widget = widget->native_widget_private(); @@ -1821,6 +1806,14 @@ TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_DesktopNativeWidget) { #endif // !OS_CHROMEOS TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { + // This test doesn't work in mus as it assumes widget and GetContext() + // share an aura hierarchy. Test coverage of deletion from mouse pressed is + // important though and should be added, hence the NOTIMPLEMENTED(). + // http://crbug.com/594260. + if (IsMus()) { + NOTIMPLEMENTED(); + return; + } Widget* widget = new Widget; Widget::InitParams params = CreateParams(views::Widget::InitParams::TYPE_POPUP); @@ -1844,6 +1837,9 @@ TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { #if !defined(OS_MACOSX) || defined(USE_AURA) TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) { + // This test doesn't make sense for mus. Force NativeWidgetAura to be used. + DisableNativeWidgetMus(); + Widget* widget = new Widget; Widget::InitParams params = CreateParams(views::Widget::InitParams::TYPE_POPUP); @@ -1895,7 +1891,8 @@ bool RunGetNativeThemeFromDestructor(const Widget::InitParams& in_params, params.delegate = new GetNativeThemeFromDestructorView; #if !defined(OS_CHROMEOS) if (is_first_run) { - params.native_widget = new PlatformDesktopNativeWidget(widget.get()); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, widget.get(), nullptr); needs_second_run = true; } #endif @@ -1979,7 +1976,8 @@ TEST_F(WidgetTest, CloseDestroys) { CreateParams(views::Widget::InitParams::TYPE_MENU); params.opacity = Widget::InitParams::OPAQUE_WINDOW; #if !defined(OS_CHROMEOS) - params.native_widget = new PlatformDesktopNativeWidget(widget); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, widget, nullptr); #endif widget->Init(params); widget->Show(); @@ -2906,8 +2904,10 @@ class WidgetChildDestructionTest : public WidgetTest { Widget::InitParams params = CreateParams(views::Widget::InitParams::TYPE_WINDOW); #if !defined(OS_CHROMEOS) - if (top_level_has_desktop_native_widget_aura) - params.native_widget = new PlatformDesktopNativeWidget(top_level); + if (top_level_has_desktop_native_widget_aura) { + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, top_level, nullptr); + } #endif top_level->Init(params); top_level->GetRootView()->AddChildView( @@ -2919,8 +2919,10 @@ class WidgetChildDestructionTest : public WidgetTest { CreateParams(views::Widget::InitParams::TYPE_POPUP); child_params.parent = top_level->GetNativeView(); #if !defined(OS_CHROMEOS) - if (child_has_desktop_native_widget_aura) - child_params.native_widget = new PlatformDesktopNativeWidget(child); + if (child_has_desktop_native_widget_aura) { + child_params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(child_params, child, nullptr); + } #endif child->Init(child_params); child->GetRootView()->AddChildView( @@ -2970,27 +2972,34 @@ TEST_F(WidgetTest, FullscreenStatePropagated) { init_params.bounds = gfx::Rect(0, 0, 500, 500); init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - { - Widget top_level_widget; - top_level_widget.Init(init_params); - top_level_widget.SetFullscreen(true); - EXPECT_EQ(top_level_widget.IsVisible(), - IsNativeWindowVisible(top_level_widget.GetNativeWindow())); - top_level_widget.CloseNow(); - } + Widget top_level_widget; + top_level_widget.Init(init_params); + top_level_widget.SetFullscreen(true); + EXPECT_EQ(top_level_widget.IsVisible(), + IsNativeWindowVisible(top_level_widget.GetNativeWindow())); + top_level_widget.CloseNow(); +} + +// Verifies nativeview visbility matches that of Widget visibility when +// SetFullscreen is invoked, for a widget provided with a desktop widget. #if !defined(OS_CHROMEOS) - { - Widget top_level_widget; - init_params.native_widget = - new PlatformDesktopNativeWidget(&top_level_widget); - top_level_widget.Init(init_params); - top_level_widget.SetFullscreen(true); - EXPECT_EQ(top_level_widget.IsVisible(), - IsNativeWindowVisible(top_level_widget.GetNativeWindow())); - top_level_widget.CloseNow(); - } -#endif +TEST_F(WidgetTest, FullscreenStatePropagated_DesktopWidget) { + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.show_state = ui::SHOW_STATE_NORMAL; + init_params.bounds = gfx::Rect(0, 0, 500, 500); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + Widget top_level_widget; + init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl( + init_params, &top_level_widget, nullptr); + + top_level_widget.Init(init_params); + top_level_widget.SetFullscreen(true); + EXPECT_EQ(top_level_widget.IsVisible(), + IsNativeWindowVisible(top_level_widget.GetNativeWindow())); + top_level_widget.CloseNow(); } +#endif namespace { @@ -3045,12 +3054,7 @@ TEST_F(WidgetTest, FullscreenFrameLayout) { widget->Show(); RunPendingMessages(); - if (IsTestingSnowLeopard()) { - // Fullscreen is currently ignored on Snow Leopard. - EXPECT_FALSE(frame->fullscreen_layout_called()); - } else { - EXPECT_TRUE(frame->fullscreen_layout_called()); - } + EXPECT_TRUE(frame->fullscreen_layout_called()); } #if !defined(OS_CHROMEOS) @@ -3078,7 +3082,8 @@ TEST_F(WidgetTest, IsActiveFromDestroy) { Widget parent_widget; Widget::InitParams parent_params = CreateParams(Widget::InitParams::TYPE_POPUP); - parent_params.native_widget = new PlatformDesktopNativeWidget(&parent_widget); + parent_params.native_widget = CreatePlatformDesktopNativeWidgetImpl( + parent_params, &parent_widget, nullptr); parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; parent_widget.Init(parent_params); parent_widget.Show(); @@ -3183,37 +3188,13 @@ TEST_F(WidgetTest, NonClientWindowValidAfterInit) { } #if defined(OS_WIN) -// This test validates that sending WM_CHAR/WM_SYSCHAR/WM_SYSDEADCHAR -// messages via the WindowEventTarget interface implemented by the -// HWNDMessageHandler class does not cause a crash due to an unprocessed -// event -TEST_F(WidgetTest, CharMessagesAsKeyboardMessagesDoesNotCrash) { - Widget widget; - Widget::InitParams params = - CreateParams(Widget::InitParams::TYPE_WINDOW); - params.native_widget = new PlatformDesktopNativeWidget(&widget); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget.Init(params); - widget.Show(); - - ui::WindowEventTarget* target = - reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue( - widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(), - ui::WindowEventTarget::kWin32InputEventTarget)); - EXPECT_NE(nullptr, target); - bool handled = false; - target->HandleKeyboardMessage(WM_CHAR, 0, 0, &handled); - target->HandleKeyboardMessage(WM_SYSCHAR, 0, 0, &handled); - target->HandleKeyboardMessage(WM_SYSDEADCHAR, 0, 0, &handled); - widget.CloseNow(); -} - // Provides functionality to subclass a window and keep track of messages // received. class SubclassWindowHelper { public: explicit SubclassWindowHelper(HWND window) - : window_(window) { + : window_(window), + message_to_destroy_on_(0) { EXPECT_EQ(instance_, nullptr); instance_ = this; EXPECT_TRUE(Subclass()); @@ -3233,6 +3214,10 @@ class SubclassWindowHelper { messages_.clear(); } + void set_message_to_destroy_on(unsigned int message) { + message_to_destroy_on_ = message; + } + private: bool Subclass() { old_proc_ = reinterpret_cast<WNDPROC>( @@ -3258,14 +3243,20 @@ class SubclassWindowHelper { // Keep track of messags received for this window. instance_->messages_.insert(message); - return ::CallWindowProc(instance_->old_proc_, window, message, w_param, - l_param); + LRESULT ret = ::CallWindowProc(instance_->old_proc_, window, message, + w_param, l_param); + if (message == instance_->message_to_destroy_on_) { + instance_->Unsubclass(); + ::DestroyWindow(window); + } + return ret; } WNDPROC old_proc_; HWND window_; static SubclassWindowHelper* instance_; std::set<unsigned int> messages_; + unsigned int message_to_destroy_on_; DISALLOW_COPY_AND_ASSIGN(SubclassWindowHelper); }; @@ -3278,11 +3269,13 @@ SubclassWindowHelper* SubclassWindowHelper::instance_ = nullptr; // 1. Posting a WM_NCMOUSEMOVE message for a different location. // 2. Posting a WM_NCMOUSEMOVE message with a different hittest code. // 3. Posting a WM_MOUSEMOVE message. -TEST_F(WidgetTest, SysCommandMoveOnNCLButtonDownOnCaptionAndMoveTest) { +// Disabled because of flaky timeouts: http://crbug.com/592742 +TEST_F(WidgetTest, DISABLED_SysCommandMoveOnNCLButtonDownOnCaptionAndMoveTest) { Widget widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.native_widget = new PlatformDesktopNativeWidget(&widget); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, &widget, nullptr); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget.Init(params); widget.SetBounds(gfx::Rect(0, 0, 200, 200)); @@ -3347,10 +3340,49 @@ TEST_F(WidgetTest, SysCommandMoveOnNCLButtonDownOnCaptionAndMoveTest) { widget.CloseNow(); } + +// This test validates that destroying the window in the context of the +// WM_SYSCOMMAND message with SC_MOVE does not crash. +// Disabled because of flaky timeouts: http://crbug.com/592742 +TEST_F(WidgetTest, DISABLED_DestroyInSysCommandNCLButtonDownOnCaption) { + Widget widget; + Widget::InitParams params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + params.native_widget = + CreatePlatformDesktopNativeWidgetImpl(params, &widget, nullptr); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget.Init(params); + widget.SetBounds(gfx::Rect(0, 0, 200, 200)); + widget.Show(); + ::SetCursorPos(500, 500); + + HWND window = widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + + SubclassWindowHelper subclass_helper(window); + + // Destroying the window in the context of the WM_SYSCOMMAND message + // should not crash. + subclass_helper.set_message_to_destroy_on(WM_SYSCOMMAND); + + ::PostMessage(window, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(100, 100)); + ::PostMessage(window, WM_NCMOUSEMOVE, HTCAPTION, MAKELPARAM(110, 110)); + RunPendingMessages(); + + EXPECT_TRUE(subclass_helper.received_message(WM_NCLBUTTONDOWN)); + EXPECT_TRUE(subclass_helper.received_message(WM_SYSCOMMAND)); + + widget.CloseNow(); +} + #endif // Test that SetAlwaysOnTop and IsAlwaysOnTop are consistent. TEST_F(WidgetTest, AlwaysOnTop) { + if (IsMus()) { + NOTIMPLEMENTED(); + return; + } + WidgetAutoclosePtr widget(CreateTopLevelNativeWidget()); EXPECT_FALSE(widget->IsAlwaysOnTop()); widget->SetAlwaysOnTop(true); @@ -3401,5 +3433,78 @@ TEST_F(WidgetTest, OnDeviceScaleFactorChanged) { EXPECT_EQ(scale_factor, view->last_scale_factor()); } +namespace { + +class TestWidgetRemovalsObserver : public WidgetRemovalsObserver { + public: + TestWidgetRemovalsObserver() {} + ~TestWidgetRemovalsObserver() override {} + + void OnWillRemoveView(Widget* widget, View* view) override { + removed_views_.insert(view); + } + + bool DidRemoveView(View* view) { + return removed_views_.find(view) != removed_views_.end(); + } + + private: + std::set<View*> removed_views_; + + DISALLOW_COPY_AND_ASSIGN(TestWidgetRemovalsObserver); +}; + +} + +// Test that WidgetRemovalsObserver::OnWillRemoveView is called when deleting +// a view. +TEST_F(WidgetTest, WidgetRemovalsObserverCalled) { + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); + TestWidgetRemovalsObserver removals_observer; + widget->AddRemovalsObserver(&removals_observer); + + View* parent = new View(); + widget->client_view()->AddChildView(parent); + + View* child = new View(); + parent->AddChildView(child); + + widget->client_view()->RemoveChildView(parent); + EXPECT_TRUE(removals_observer.DidRemoveView(parent)); + EXPECT_FALSE(removals_observer.DidRemoveView(child)); + + // Calling RemoveChildView() doesn't delete the view, but deleting + // |parent| will automatically delete |child|. + delete parent; + + widget->RemoveRemovalsObserver(&removals_observer); +} + +// Test that WidgetRemovalsObserver::OnWillRemoveView is called when moving +// a view from one widget to another, but not when moving a view within +// the same widget. +TEST_F(WidgetTest, WidgetRemovalsObserverCalledWhenMovingBetweenWidgets) { + WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); + TestWidgetRemovalsObserver removals_observer; + widget->AddRemovalsObserver(&removals_observer); + + View* parent = new View(); + widget->client_view()->AddChildView(parent); + + View* child = new View(); + widget->client_view()->AddChildView(child); + + // Reparenting the child shouldn't call the removals observer. + parent->AddChildView(child); + EXPECT_FALSE(removals_observer.DidRemoveView(child)); + + // Moving the child to a different widget should call the removals observer. + WidgetAutoclosePtr widget2(CreateTopLevelPlatformWidget()); + widget2->client_view()->AddChildView(child); + EXPECT_TRUE(removals_observer.DidRemoveView(child)); + + widget->RemoveRemovalsObserver(&removals_observer); +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/win/fullscreen_handler.cc b/chromium/ui/views/win/fullscreen_handler.cc index 2f7d0775caf..0529f6e37d8 100644 --- a/chromium/ui/views/win/fullscreen_handler.cc +++ b/chromium/ui/views/win/fullscreen_handler.cc @@ -5,7 +5,9 @@ #include "ui/views/win/fullscreen_handler.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/win/win_util.h" +#include "ui/base/win/shell.h" #include "ui/gfx/geometry/rect.h" #include "ui/views/win/scoped_fullscreen_visibility.h" @@ -14,11 +16,7 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // FullscreenHandler, public: -FullscreenHandler::FullscreenHandler() - : hwnd_(NULL), - fullscreen_(false), - metro_snap_(false) { -} +FullscreenHandler::FullscreenHandler() : hwnd_(NULL), fullscreen_(false) {} FullscreenHandler::~FullscreenHandler() { } @@ -27,15 +25,7 @@ void FullscreenHandler::SetFullscreen(bool fullscreen) { if (fullscreen_ == fullscreen) return; - SetFullscreenImpl(fullscreen, false); -} - -void FullscreenHandler::SetMetroSnap(bool metro_snap) { - if (metro_snap_ == metro_snap) - return; - - SetFullscreenImpl(metro_snap, true); - metro_snap_ = metro_snap; + SetFullscreenImpl(fullscreen); } gfx::Rect FullscreenHandler::GetRestoreBounds() const { @@ -45,8 +35,14 @@ gfx::Rect FullscreenHandler::GetRestoreBounds() const { //////////////////////////////////////////////////////////////////////////////// // FullscreenHandler, private: -void FullscreenHandler::SetFullscreenImpl(bool fullscreen, bool for_metro) { - ScopedFullscreenVisibility visibility(hwnd_); +void FullscreenHandler::SetFullscreenImpl(bool fullscreen) { + scoped_ptr<ScopedFullscreenVisibility> visibility; + + // With Aero enabled disabling the visibility causes the window to disappear + // for several frames, which looks worse than doing other updates + // non-atomically. + if (!ui::win::IsAeroGlassEnabled()) + visibility.reset(new ScopedFullscreenVisibility(hwnd_)); // Save current window state if not already fullscreen. if (!fullscreen_) { @@ -73,16 +69,14 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen, bool for_metro) { // On expand, if we're given a window_rect, grow to it, otherwise do // not resize. - if (!for_metro) { - MONITORINFO monitor_info; - monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST), - &monitor_info); - gfx::Rect window_rect(monitor_info.rcMonitor); - SetWindowPos(hwnd_, NULL, window_rect.x(), window_rect.y(), - window_rect.width(), window_rect.height(), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); - } + MONITORINFO monitor_info; + monitor_info.cbSize = sizeof(monitor_info); + GetMonitorInfo(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST), + &monitor_info); + gfx::Rect window_rect(monitor_info.rcMonitor); + SetWindowPos(hwnd_, NULL, window_rect.x(), window_rect.y(), + window_rect.width(), window_rect.height(), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); } else { // Reset original window style and size. The multiple window size/moves // here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be @@ -90,13 +84,11 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen, bool for_metro) { SetWindowLong(hwnd_, GWL_STYLE, saved_window_info_.style); SetWindowLong(hwnd_, GWL_EXSTYLE, saved_window_info_.ex_style); - if (!for_metro) { - // On restore, resize to the previous saved rect size. - gfx::Rect new_rect(saved_window_info_.window_rect); - SetWindowPos(hwnd_, NULL, new_rect.x(), new_rect.y(), - new_rect.width(), new_rect.height(), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); - } + // On restore, resize to the previous saved rect size. + gfx::Rect new_rect(saved_window_info_.window_rect); + SetWindowPos(hwnd_, NULL, new_rect.x(), new_rect.y(), new_rect.width(), + new_rect.height(), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); if (saved_window_info_.maximized) ::SendMessage(hwnd_, WM_SYSCOMMAND, SC_MAXIMIZE, 0); } diff --git a/chromium/ui/views/win/fullscreen_handler.h b/chromium/ui/views/win/fullscreen_handler.h index 176cbed5b6f..b23b1421374 100644 --- a/chromium/ui/views/win/fullscreen_handler.h +++ b/chromium/ui/views/win/fullscreen_handler.h @@ -25,12 +25,10 @@ class FullscreenHandler { void set_hwnd(HWND hwnd) { hwnd_ = hwnd; } void SetFullscreen(bool fullscreen); - void SetMetroSnap(bool metro_snap); gfx::Rect GetRestoreBounds() const; bool fullscreen() const { return fullscreen_; } - bool metro_snap() const { return metro_snap_; } private: // Information saved before going into fullscreen mode, used to restore the @@ -42,11 +40,10 @@ class FullscreenHandler { RECT window_rect; }; - void SetFullscreenImpl(bool fullscreen, bool for_metro); + void SetFullscreenImpl(bool fullscreen); HWND hwnd_; bool fullscreen_; - bool metro_snap_; // Saved window information from before entering fullscreen mode. // TODO(beng): move to private once GetRestoredBounds() moves onto Widget. @@ -57,4 +54,4 @@ class FullscreenHandler { } // namespace views -#endif // UI_VIEWS_WIN_FULLSCREEN_HANDLER_H_
\ No newline at end of file +#endif // UI_VIEWS_WIN_FULLSCREEN_HANDLER_H_ diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index 0cacffe4a76..74d5cd6eb4b 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -8,7 +8,8 @@ #include <oleacc.h> #include <shellapi.h> #include <tchar.h> -#include <tpcshrd.h> + +#include <utility> #include "base/bind.h" #include "base/bind_helpers.h" @@ -37,6 +38,7 @@ #include "ui/gfx/win/direct_manipulation.h" #include "ui/gfx/win/dpi.h" #include "ui/gfx/win/hwnd_util.h" +#include "ui/gfx/win/rendering_window_manager.h" #include "ui/native_theme/native_theme_win.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/monitor_win.h" @@ -149,13 +151,10 @@ LRESULT CALLBACK MoveLoopMouseWatcher::KeyHook(int n_code, WPARAM w_param, LPARAM l_param) { if (n_code == HC_ACTION && w_param == VK_ESCAPE) { - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - int value = TRUE; - DwmSetWindowAttribute(instance_->host_->hwnd(), - DWMWA_TRANSITIONS_FORCEDISABLED, - &value, - sizeof(value)); - } + int value = TRUE; + DwmSetWindowAttribute(instance_->host_->hwnd(), + DWMWA_TRANSITIONS_FORCEDISABLED, &value, + sizeof(value)); if (instance_->hide_on_escape_) instance_->host_->Hide(); } @@ -313,7 +312,6 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) delegate_(delegate), fullscreen_handler_(new FullscreenHandler), waiting_for_close_now_(false), - remove_standard_frame_(false), use_system_default_icon_(false), restored_enabled_(false), current_cursor_(NULL), @@ -333,6 +331,7 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) dwm_transition_desired_(false), sent_window_size_changing_(false), left_button_down_on_caption_(false), + background_fullscreen_hack_(false), autohide_factory_(this), weak_factory_(this) {} @@ -385,16 +384,7 @@ void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { direct_manipulation_helper_->Initialize(hwnd()); // Disable pen flicks (http://crbug.com/506977) - if (base::win::GetVersion() >= base::win::VERSION_WIN7) { - ATOM atom = ::GlobalAddAtom(MICROSOFT_TABLETPENSERVICE_PROPERTY); - DCHECK(atom); - - ::SetProp(hwnd(), MICROSOFT_TABLETPENSERVICE_PROPERTY, - reinterpret_cast<HANDLE>(TABLET_DISABLE_FLICKS | - TABLET_DISABLE_FLICKFALLBACKKEYS)); - - ::GlobalDeleteAtom(atom); - } + base::win::DisableFlicks(hwnd()); } void HWNDMessageHandler::InitModalType(ui::ModalType modal_type) { @@ -421,10 +411,8 @@ void HWNDMessageHandler::Close() { // they can activate as foreground windows upon this window's destruction. RestoreEnabledIfNecessary(); - // Remove the property which disables pen flicks (http://crbug.com/506977) - // for this window. - if (base::win::GetVersion() >= base::win::VERSION_WIN7) - ::RemoveProp(hwnd(), MICROSOFT_TABLETPENSERVICE_PROPERTY); + // Re-enable flicks which removes the window property. + base::win::EnableFlicks(hwnd()); if (!waiting_for_close_now_) { // And we delay the close so that if we are called from an ATL callback, @@ -465,7 +453,7 @@ gfx::Rect HWNDMessageHandler::GetClientAreaBoundsInScreen() const { gfx::Rect HWNDMessageHandler::GetRestoredBounds() const { // If we're in fullscreen mode, we've changed the normal bounds to the monitor // rect, so return the saved bounds instead. - if (fullscreen_handler_->fullscreen()) + if (IsFullscreen()) return fullscreen_handler_->GetRestoreBounds(); gfx::Rect bounds; @@ -525,25 +513,8 @@ void HWNDMessageHandler::GetWindowPlacement( void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds_in_pixels, bool force_size_changed) { - LONG style = GetWindowLong(hwnd(), GWL_STYLE); - if (style & WS_MAXIMIZE) - SetWindowLong(hwnd(), GWL_STYLE, style & ~WS_MAXIMIZE); - - gfx::Size old_size = GetClientAreaBounds().size(); - SetWindowPos(hwnd(), NULL, bounds_in_pixels.x(), bounds_in_pixels.y(), - bounds_in_pixels.width(), bounds_in_pixels.height(), - SWP_NOACTIVATE | SWP_NOZORDER); - - // If HWND size is not changed, we will not receive standard size change - // notifications. If |force_size_changed| is |true|, we should pretend size is - // changed. - if (old_size == bounds_in_pixels.size() && force_size_changed) { - delegate_->HandleClientSizeChanged(GetClientAreaBounds().size()); - ResetWindowRegion(false, true); - } - - if (direct_manipulation_helper_) - direct_manipulation_helper_->SetBounds(bounds_in_pixels); + background_fullscreen_hack_ = false; + SetBoundsInternal(bounds_in_pixels, force_size_changed); } void HWNDMessageHandler::SetSize(const gfx::Size& size) { @@ -715,6 +686,10 @@ bool HWNDMessageHandler::IsMaximized() const { return !!::IsZoomed(hwnd()); } +bool HWNDMessageHandler::IsFullscreen() const { + return fullscreen_handler_->fullscreen(); +} + bool HWNDMessageHandler::IsAlwaysOnTop() const { return (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; } @@ -779,11 +754,9 @@ bool HWNDMessageHandler::HasCapture() const { } void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) { - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - int dwm_value = enabled ? FALSE : TRUE; - DwmSetWindowAttribute( - hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, sizeof(dwm_value)); - } + int dwm_value = enabled ? FALSE : TRUE; + DwmSetWindowAttribute(hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, + sizeof(dwm_value)); } bool HWNDMessageHandler::SetTitle(const base::string16& title) { @@ -812,38 +785,31 @@ void HWNDMessageHandler::SetCursor(HCURSOR cursor) { } void HWNDMessageHandler::FrameTypeChanged() { - if (base::win::GetVersion() < base::win::VERSION_VISTA) { - // Don't redraw the window here, because we invalidate the window later. - ResetWindowRegion(true, false); - // The non-client view needs to update too. - delegate_->HandleFrameChanged(); - InvalidateRect(hwnd(), NULL, FALSE); - } else { - if (!custom_window_region_.is_valid() && !delegate_->IsUsingCustomFrame()) - dwm_transition_desired_ = true; - if (!dwm_transition_desired_ || !fullscreen_handler_->fullscreen()) - PerformDwmTransition(); - } + if (!custom_window_region_.is_valid() && + delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) + dwm_transition_desired_ = true; + if (!dwm_transition_desired_ || !IsFullscreen()) + PerformDwmTransition(); } void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { if (!window_icon.isNull()) { - base::win::ScopedHICON previous_icon = window_icon_.Pass(); - window_icon_ = - IconUtil::CreateHICONFromSkBitmap(*window_icon.bitmap()).Pass(); + base::win::ScopedHICON previous_icon = std::move(window_icon_); + window_icon_ = IconUtil::CreateHICONFromSkBitmap(*window_icon.bitmap()); SendMessage(hwnd(), WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(window_icon_.get())); } if (!app_icon.isNull()) { - base::win::ScopedHICON previous_icon = app_icon_.Pass(); - app_icon_ = IconUtil::CreateHICONFromSkBitmap(*app_icon.bitmap()).Pass(); + base::win::ScopedHICON previous_icon = std::move(app_icon_); + app_icon_ = IconUtil::CreateHICONFromSkBitmap(*app_icon.bitmap()); SendMessage(hwnd(), WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(app_icon_.get())); } } void HWNDMessageHandler::SetFullscreen(bool fullscreen) { + background_fullscreen_hack_ = false; fullscreen_handler()->SetFullscreen(fullscreen); // If we are out of fullscreen and there was a pending DWM transition for the // window, then go ahead and do it now. @@ -875,6 +841,13 @@ void HWNDMessageHandler::SizeConstraintsChanged() { SetWindowLong(hwnd(), GWL_STYLE, style); } +bool HWNDMessageHandler::HasChildRenderingWindow() { + // This can change dynamically if the system switches between GPU and + // software rendering. + return gfx::RenderingWindowManager::GetInstance()->HasValidChildWindow( + hwnd()); +} + //////////////////////////////////////////////////////////////////////////////// // HWNDMessageHandler, gfx::WindowImpl overrides: @@ -929,7 +902,8 @@ LRESULT HWNDMessageHandler::OnWndProc(UINT message, } if (message == WM_ACTIVATE && IsTopLevelWindow(window)) - PostProcessActivateMessage(LOWORD(w_param), !!HIWORD(w_param)); + PostProcessActivateMessage(LOWORD(w_param), !!HIWORD(w_param), + reinterpret_cast<HWND>(l_param)); return result; } @@ -1027,12 +1001,50 @@ void HWNDMessageHandler::SetInitialFocus() { } } -void HWNDMessageHandler::PostProcessActivateMessage(int activation_state, - bool minimized) { +void HWNDMessageHandler::PostProcessActivateMessage( + int activation_state, + bool minimized, + HWND window_gaining_or_losing_activation) { DCHECK(IsTopLevelWindow(hwnd())); const bool active = activation_state != WA_INACTIVE && !minimized; if (delegate_->CanActivate()) delegate_->HandleActivationChanged(active); + + if (!::IsWindow(window_gaining_or_losing_activation)) + window_gaining_or_losing_activation = ::GetForegroundWindow(); + + // If the window losing activation is a fullscreen window, we reduce the size + // of the window by 1px. i.e. Not fullscreen. This is to work around an + // apparent bug in the Windows taskbar where in it tracks fullscreen state on + // a per thread basis. This causes it not be a topmost window when any + // maximized window on a thread which has a fullscreen window is active. This + // affects the way these windows interact with the taskbar, they obscure it + // when maximized, autohide does not work correctly, etc. + // By reducing the size of the fullscreen window by 1px, we ensure that the + // taskbar no longer treats the window and in turn the thread as a fullscreen + // thread. This in turn ensures that maximized windows on the same thread + /// don't obscure the taskbar, etc. + if (!active) { + if (IsFullscreen() && ::IsWindow(window_gaining_or_losing_activation)) { + // Reduce the bounds of the window by 1px to ensure that Windows does + // not treat this like a fullscreen window. + MONITORINFO monitor_info = {sizeof(monitor_info)}; + GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), + &monitor_info); + gfx::Rect shrunk_rect(monitor_info.rcMonitor); + shrunk_rect.set_height(shrunk_rect.height() - 1); + background_fullscreen_hack_ = true; + SetBoundsInternal(shrunk_rect, false); + } + } else if (background_fullscreen_hack_) { + // Restore the bounds of the window to fullscreen. + DCHECK(IsFullscreen()); + MONITORINFO monitor_info = {sizeof(monitor_info)}; + GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), + &monitor_info); + SetBoundsInternal(gfx::Rect(monitor_info.rcMonitor), false); + background_fullscreen_hack_ = false; + } } void HWNDMessageHandler::RestoreEnabledIfNecessary() { @@ -1078,6 +1090,9 @@ void HWNDMessageHandler::TrackMouseEvents(DWORD mouse_tracking_flags) { } void HWNDMessageHandler::ClientAreaSizeChanged() { + // Ignore size changes due to fullscreen windows losing activation. + if (background_fullscreen_hack_) + return; gfx::Size s = GetClientAreaBounds().size(); delegate_->HandleClientSizeChanged(s); } @@ -1089,16 +1104,14 @@ bool HWNDMessageHandler::GetClientAreaInsets(gfx::Insets* insets) const { // Returning false causes the default handling in OnNCCalcSize() to // be invoked. - if (!delegate_->IsWidgetWindow() || - (!delegate_->IsUsingCustomFrame() && !remove_standard_frame_)) { + if (!delegate_->HasNonClientView() || HasSystemFrame()) return false; - } if (IsMaximized()) { // Windows automatically adds a standard width border to all sides when a // window is maximized. int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); - if (remove_standard_frame_) + if (!delegate_->HasFrame()) border_thickness -= 1; *insets = gfx::Insets( border_thickness, border_thickness, border_thickness, border_thickness); @@ -1118,7 +1131,8 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { // the delegate to allow for a custom hit mask. if ((window_ex_style() & WS_EX_COMPOSITED) == 0 && !custom_window_region_.is_valid() && - (!delegate_->IsUsingCustomFrame() || !delegate_->IsWidgetWindow())) { + (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN || + !delegate_->HasNonClientView())) { if (force) SetWindowRgn(hwnd(), NULL, redraw); return; @@ -1162,14 +1176,12 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { } void HWNDMessageHandler::UpdateDwmNcRenderingPolicy() { - if (base::win::GetVersion() < base::win::VERSION_VISTA) - return; - - if (fullscreen_handler_->fullscreen()) + if (IsFullscreen()) return; DWMNCRENDERINGPOLICY policy = - custom_window_region_.is_valid() || delegate_->IsUsingCustomFrame() + custom_window_region_.is_valid() || + delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN ? DWMNCRP_DISABLED : DWMNCRP_ENABLED; @@ -1228,14 +1240,19 @@ void HWNDMessageHandler::ForceRedrawWindow(int attempts) { InvalidateRect(hwnd(), NULL, FALSE); } +bool HWNDMessageHandler::HasSystemFrame() const { + return delegate_->HasFrame() && + delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN; +} + // Message handlers ------------------------------------------------------------ void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) { - if (delegate_->IsWidgetWindow() && !active && + if (delegate_->HasNonClientView() && !active && thread_id != GetCurrentThreadId()) { delegate_->HandleAppDeactivated(); // Also update the native frame if it is rendering the non-client area. - if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) + if (HasSystemFrame()) DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0); } } @@ -1277,12 +1294,10 @@ void HWNDMessageHandler::OnCommand(UINT notification_code, LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { if (window_ex_style() & WS_EX_COMPOSITED) { - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - // This is part of the magic to emulate layered windows with Aura - // see the explanation elsewere when we set WS_EX_COMPOSITED style. - MARGINS margins = {-1,-1,-1,-1}; - DwmExtendFrameIntoClientArea(hwnd(), &margins); - } + // This is part of the magic to emulate layered windows with Aura + // see the explanation elsewere when we set WS_EX_COMPOSITED style. + MARGINS margins = {-1, -1, -1, -1}; + DwmExtendFrameIntoClientArea(hwnd(), &margins); } fullscreen_handler_->set_hwnd(hwnd()); @@ -1294,7 +1309,7 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); - if (remove_standard_frame_) { + if (!delegate_->HasFrame()) { SetWindowLong(hwnd(), GWL_STYLE, GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION); SendFrameChanged(); @@ -1303,8 +1318,7 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { // Get access to a modifiable copy of the system menu. GetSystemMenu(hwnd(), false); - if (base::win::GetVersion() >= base::win::VERSION_WIN7 && - ui::AreTouchEventsEnabled()) + if (ui::AreTouchEventsEnabled()) RegisterTouchWindow(hwnd(), TWF_WANTPALM); // We need to allow the delegate to size its contents since the window may not @@ -1330,13 +1344,15 @@ void HWNDMessageHandler::OnDestroy() { void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel, const gfx::Size& screen_size) { delegate_->HandleDisplayChange(); + // Force a WM_NCCALCSIZE to occur to ensure that we handle auto hide + // taskbars correctly. SendFrameChanged(); } LRESULT HWNDMessageHandler::OnDwmCompositionChanged(UINT msg, WPARAM w_param, LPARAM l_param) { - if (!delegate_->IsWidgetWindow()) { + if (!delegate_->HasNonClientView()) { SetMsgHandled(FALSE); return 0; } @@ -1457,7 +1473,7 @@ LRESULT HWNDMessageHandler::OnImeMessages(UINT message, } void HWNDMessageHandler::OnInitMenu(HMENU menu) { - bool is_fullscreen = fullscreen_handler_->fullscreen(); + bool is_fullscreen = IsFullscreen(); bool is_minimized = IsMinimized(); bool is_maximized = IsMaximized(); bool is_restored = !is_fullscreen && !is_minimized && !is_maximized; @@ -1489,7 +1505,10 @@ LRESULT HWNDMessageHandler::OnKeyEvent(UINT message, MSG msg = { hwnd(), message, w_param, l_param, static_cast<DWORD>(GetMessageTime())}; ui::KeyEvent key(msg); + base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); delegate_->HandleKeyEvent(&key); + if (!ref) + return 0; if (!key.handled()) SetMsgHandled(FALSE); return 0; @@ -1519,24 +1538,10 @@ LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow); return MA_NOACTIVATE; } - // A child window activation should be treated as if we lost activation. - POINT cursor_pos = {0}; - ::GetCursorPos(&cursor_pos); - ::ScreenToClient(hwnd(), &cursor_pos); - // The code below exists for child windows like NPAPI plugins etc which need - // to be activated whenever we receive a WM_MOUSEACTIVATE message. Don't put - // transparent child windows in this bucket as they are not supposed to grab - // activation. - // TODO(ananta) - // Get rid of this code when we deprecate NPAPI plugins. - HWND child = ::RealChildWindowFromPoint(hwnd(), cursor_pos); - if (::IsWindow(child) && child != hwnd() && ::IsWindowVisible(child) && - !(::GetWindowLong(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) - PostProcessActivateMessage(WA_INACTIVE, false); // TODO(beng): resolve this with the GetWindowLong() check on the subsequent // line. - if (delegate_->IsWidgetWindow()) { + if (delegate_->HasNonClientView()) { if (delegate_->CanActivate()) return MA_ACTIVATE; if (delegate_->WantsMouseEventsWhenInactive()) @@ -1575,9 +1580,9 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, // cleared before it is converted to BOOL. BOOL active = static_cast<BOOL>(LOWORD(w_param)); - bool inactive_rendering_disabled = delegate_->IsInactiveRenderingDisabled(); + bool render_as_active = delegate_->IsAlwaysRenderAsActive(); - if (!delegate_->IsWidgetWindow()) { + if (!delegate_->HasNonClientView()) { SetMsgHandled(FALSE); return 0; } @@ -1586,10 +1591,10 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, return TRUE; // On activation, lift any prior restriction against rendering as inactive. - if (active && inactive_rendering_disabled) - delegate_->EnableInactiveRendering(); + if (active && render_as_active) + delegate_->SetAlwaysRenderAsActive(false); - if (delegate_->IsUsingCustomFrame()) { + if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { // TODO(beng, et al): Hack to redraw this window and child windows // synchronously upon activation. Not all child windows are redrawing // themselves leading to issues like http://crbug.com/74604 @@ -1606,16 +1611,13 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, if (IsVisible()) delegate_->SchedulePaint(); - // Avoid DefWindowProc non-client rendering over our custom frame on newer - // Windows versions only (breaks taskbar activation indication on XP/Vista). - if (delegate_->IsUsingCustomFrame() && - base::win::GetVersion() > base::win::VERSION_VISTA) { + if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { SetMsgHandled(TRUE); return TRUE; } return DefWindowProcWithRedrawLock( - WM_NCACTIVATE, inactive_rendering_disabled || active, 0); + WM_NCACTIVATE, render_as_active || active, 0); } LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { @@ -1638,8 +1640,7 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { gfx::Insets insets; bool got_insets = GetClientAreaInsets(&insets); - if (!got_insets && !fullscreen_handler_->fullscreen() && - !(mode && remove_standard_frame_)) { + if (!got_insets && !IsFullscreen() && !(mode && !delegate_->HasFrame())) { SetMsgHandled(FALSE); return 0; } @@ -1678,7 +1679,7 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { if (autohide_edges & ViewsDelegate::EDGE_LEFT) client_rect->left += kAutoHideTaskbarThicknessPx; if (autohide_edges & ViewsDelegate::EDGE_TOP) { - if (!delegate_->IsUsingCustomFrame()) { + if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) { // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of // WM_NCHITTEST, having any nonclient area atop the window causes the // caption buttons to draw onscreen but not respond to mouse @@ -1717,14 +1718,14 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { } LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) { - if (!delegate_->IsWidgetWindow()) { + if (!delegate_->HasNonClientView()) { SetMsgHandled(FALSE); return 0; } // If the DWM is rendering the window controls, we need to give the DWM's // default window procedure first chance to handle hit testing. - if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) { + if (HasSystemFrame()) { LRESULT result; if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0, MAKELPARAM(point.x(), point.y()), &result)) { @@ -1791,32 +1792,8 @@ LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) { } void HWNDMessageHandler::OnNCPaint(HRGN rgn) { - // We only do non-client painting if we're not using the native frame. - // It's required to avoid some native painting artifacts from appearing when - // the window is resized. - if (!delegate_->IsWidgetWindow() || !delegate_->IsUsingCustomFrame()) { - SetMsgHandled(FALSE); - return; - } - - // We have an NC region and need to paint it. We expand the NC region to - // include the dirty region of the root view. This is done to minimize - // paints. RECT window_rect; GetWindowRect(hwnd(), &window_rect); - - gfx::Size root_view_size = delegate_->GetRootViewSize(); - if (gfx::Size(window_rect.right - window_rect.left, - window_rect.bottom - window_rect.top) != root_view_size) { - // If the size of the window differs from the size of the root view it - // means we're being asked to paint before we've gotten a WM_SIZE. This can - // happen when the user is interactively resizing the window. To avoid - // mass flickering we don't do anything here. Once we get the WM_SIZE we'll - // reset the region of the window which triggers another WM_NCPAINT and - // all is well. - return; - } - RECT dirty_region; // A value of 1 indicates paint all. if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { @@ -1827,18 +1804,64 @@ void HWNDMessageHandler::OnNCPaint(HRGN rgn) { } else { RECT rgn_bounding_box; GetRgnBox(rgn, &rgn_bounding_box); - if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) - return; // Dirty region doesn't intersect window bounds, bale. + if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) { + SetMsgHandled(FALSE); + return; // Dirty region doesn't intersect window bounds, bail. + } // rgn_bounding_box is in screen coordinates. Map it to window coordinates. OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); } + // We only do non-client painting if we're not using the system frame. + // It's required to avoid some native painting artifacts from appearing when + // the window is resized. + if (!delegate_->HasNonClientView() || + delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) { + if (ui::win::IsAeroGlassEnabled()) { + // The default WM_NCPAINT handler under Aero Glass doesn't clear the + // nonclient area, so it'll remain the default white color. That area is + // invisible initially (covered by the window border) but can become + // temporarily visible on maximizing or fullscreening, so clear it here. + HDC dc = GetWindowDC(hwnd()); + RECT client_rect; + ::GetClientRect(hwnd(), &client_rect); + ::MapWindowPoints(hwnd(), nullptr, reinterpret_cast<POINT*>(&client_rect), + 2); + ::OffsetRect(&client_rect, -window_rect.left, -window_rect.top); + // client_rect now is in window space. + + base::win::ScopedRegion base(::CreateRectRgnIndirect(&dirty_region)); + base::win::ScopedRegion client(::CreateRectRgnIndirect(&client_rect)); + base::win::ScopedRegion nonclient(::CreateRectRgn(0, 0, 0, 0)); + ::CombineRgn(nonclient.get(), base.get(), client.get(), RGN_DIFF); + + ::SelectClipRgn(dc, nonclient.get()); + HBRUSH brush = CreateSolidBrush(0); + ::FillRect(dc, &dirty_region, brush); + ::DeleteObject(brush); + ::ReleaseDC(hwnd(), dc); + } + SetMsgHandled(FALSE); + return; + } + + gfx::Size root_view_size = delegate_->GetRootViewSize(); + if (gfx::Size(window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top) != root_view_size) { + // If the size of the window differs from the size of the root view it + // means we're being asked to paint before we've gotten a WM_SIZE. This can + // happen when the user is interactively resizing the window. To avoid + // mass flickering we don't do anything here. Once we get the WM_SIZE we'll + // reset the region of the window which triggers another WM_NCPAINT and + // all is well. + return; + } delegate_->HandlePaintAccelerated(gfx::Rect(dirty_region)); // When using a custom frame, we want to avoid calling DefWindowProc() since // that may render artifacts. - SetMsgHandled(delegate_->IsUsingCustomFrame()); + SetMsgHandled(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN); } LRESULT HWNDMessageHandler::OnNCUAHDrawCaption(UINT message, @@ -1846,7 +1869,7 @@ LRESULT HWNDMessageHandler::OnNCUAHDrawCaption(UINT message, LPARAM l_param) { // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for // an explanation about why we need to handle this message. - SetMsgHandled(delegate_->IsUsingCustomFrame()); + SetMsgHandled(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN); return 0; } @@ -1855,7 +1878,7 @@ LRESULT HWNDMessageHandler::OnNCUAHDrawFrame(UINT message, LPARAM l_param) { // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for // an explanation about why we need to handle this message. - SetMsgHandled(delegate_->IsUsingCustomFrame()); + SetMsgHandled(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN); return 0; } @@ -1892,8 +1915,18 @@ void HWNDMessageHandler::OnPaint(HDC dc) { << ", GDI peak count: " << peak_gdi_objects; } - if (!IsRectEmpty(&ps.rcPaint)) + if (!IsRectEmpty(&ps.rcPaint)) { + if (HasChildRenderingWindow()) { + // If there's a child window that's being rendered to then clear the + // area outside it (as WS_CLIPCHILDREN is set) with transparent black. + // Otherwise, other portions of the backing store for the window can + // flicker opaque black. http://crbug.com/586454 + + FillRect(ps.hdc, &ps.rcPaint, + reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH))); + } delegate_->HandlePaintAccelerated(gfx::Rect(ps.rcPaint)); + } EndPaint(hwnd(), &ps); } @@ -1988,6 +2021,13 @@ void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) { delegate_->HandleWorkAreaChanged(); SetMsgHandled(FALSE); } + + // If the work area is changing, then it could be as a result of the taskbar + // broadcasting the WM_SETTINGCHANGE message due to changes in auto hide + // settings, etc. Force a WM_NCCALCSIZE to occur to ensure that we handle + // this correctly. + if (flags == SPI_SETWORKAREA) + SendFrameChanged(); } void HWNDMessageHandler::OnSize(UINT param, const gfx::Size& size) { @@ -2019,12 +2059,11 @@ void HWNDMessageHandler::OnSysCommand(UINT notification_code, // specific information so we must exclude this when comparing. static const int sc_mask = 0xFFF0; // Ignore size/move/maximize in fullscreen mode. - if (fullscreen_handler_->fullscreen() && - (((notification_code & sc_mask) == SC_SIZE) || - ((notification_code & sc_mask) == SC_MOVE) || - ((notification_code & sc_mask) == SC_MAXIMIZE))) + if (IsFullscreen() && (((notification_code & sc_mask) == SC_SIZE) || + ((notification_code & sc_mask) == SC_MOVE) || + ((notification_code & sc_mask) == SC_MAXIMIZE))) return; - if (delegate_->IsUsingCustomFrame()) { + if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { if ((notification_code & sc_mask) == SC_MINIMIZE || (notification_code & sc_mask) == SC_MAXIMIZE || (notification_code & sc_mask) == SC_RESTORE) { @@ -2170,10 +2209,16 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { bool work_area_changed = (monitor_rect == last_monitor_rect_) && (work_area != last_work_area_); + // If the size of a background fullscreen window changes again, then we + // should reset the |background_fullscreen_hack_| flag. + if (background_fullscreen_hack_ && + (!(window_pos->flags & SWP_NOSIZE) && + (monitor_rect.height() - window_pos->cy != 1))) { + background_fullscreen_hack_ = false; + } if (monitor && (monitor == last_monitor_) && - ((fullscreen_handler_->fullscreen() && - !fullscreen_handler_->metro_snap()) || - work_area_changed)) { + ((IsFullscreen() && !background_fullscreen_hack_) || + work_area_changed)) { // A rect for the monitor we're on changed. Normally Windows notifies // us about this (and thus we're reaching here due to the SetWindowPos() // call in OnSettingChange() above), but with some software (e.g. @@ -2183,7 +2228,7 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { // response is to throw away the existing position/size information in // |window_pos| and recalculate it based on the new work rect. gfx::Rect new_window_rect; - if (fullscreen_handler_->fullscreen()) { + if (IsFullscreen()) { new_window_rect = monitor_rect; } else if (IsMaximized()) { new_window_rect = work_area; @@ -2245,7 +2290,7 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { if (DidClientAreaSizeChange(window_pos)) ClientAreaSizeChanged(); - if (remove_standard_frame_ && window_pos->flags & SWP_FRAMECHANGED && + if (!delegate_->HasFrame() && window_pos->flags & SWP_FRAMECHANGED && ui::win::IsAeroGlassEnabled() && (window_ex_style() & WS_EX_COMPOSITED) == 0) { MARGINS m = {10, 10, 10, 10}; @@ -2335,7 +2380,8 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, gfx::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point)); return 0; } - } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) { + } else if (message == WM_NCLBUTTONDOWN && + delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { switch (w_param) { case HTCLOSE: case HTMINBUTTON: @@ -2406,7 +2452,8 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, } if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU && - w_param != HTCAPTION && delegate_->IsUsingCustomFrame()) { + w_param != HTCAPTION && + delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround. // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we // need to call it inside a ScopedRedrawLock. This may cause other negative @@ -2458,7 +2505,7 @@ void HWNDMessageHandler::PerformDwmTransition() { // The non-client view needs to update too. delegate_->HandleFrameChanged(); - if (IsVisible() && !delegate_->IsUsingCustomFrame()) { + if (IsVisible() && delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) { // For some reason, we need to hide the window after we change from a custom // frame to a native frame. If we don't, the client area will be filled // with black. This seems to be related to an interaction between DWM and @@ -2557,12 +2604,15 @@ bool HWNDMessageHandler::HandleMouseInputForCaption(unsigned int message, // so we need to call it inside a ScopedRedrawLock. This may cause // other negative side-effects // (ex/ stifling non-client mouse releases). - if (delegate_->IsUsingCustomFrame()) { + // We may be deleted in the context of DefWindowProc. Don't refer to + // any member variables after the DefWindowProc call. + left_button_down_on_caption_ = false; + + if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { DefWindowProcWithRedrawLock(WM_NCLBUTTONDOWN, HTCAPTION, l_param); } else { DefWindowProc(hwnd(), WM_NCLBUTTONDOWN, HTCAPTION, l_param); } - left_button_down_on_caption_ = false; } break; } @@ -2577,5 +2627,29 @@ bool HWNDMessageHandler::HandleMouseInputForCaption(unsigned int message, return handled; } +void HWNDMessageHandler::SetBoundsInternal(const gfx::Rect& bounds_in_pixels, + bool force_size_changed) { + LONG style = GetWindowLong(hwnd(), GWL_STYLE); + if (style & WS_MAXIMIZE) + SetWindowLong(hwnd(), GWL_STYLE, style & ~WS_MAXIMIZE); + + gfx::Size old_size = GetClientAreaBounds().size(); + SetWindowPos(hwnd(), NULL, bounds_in_pixels.x(), bounds_in_pixels.y(), + bounds_in_pixels.width(), bounds_in_pixels.height(), + SWP_NOACTIVATE | SWP_NOZORDER); + + // If HWND size is not changed, we will not receive standard size change + // notifications. If |force_size_changed| is |true|, we should pretend size is + // changed. + if (old_size == bounds_in_pixels.size() && force_size_changed && + !background_fullscreen_hack_) { + delegate_->HandleClientSizeChanged(GetClientAreaBounds().size()); + ResetWindowRegion(false, true); + } + + if (direct_manipulation_helper_) + direct_manipulation_helper_->SetBounds(bounds_in_pixels); +} + } // namespace views diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index 4b7ab4179a0..143b1c61fec 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -163,6 +163,7 @@ class VIEWS_EXPORT HWNDMessageHandler : bool IsActive() const; bool IsMinimized() const; bool IsMaximized() const; + bool IsFullscreen() const; bool IsAlwaysOnTop() const; bool RunMoveLoop(const gfx::Vector2d& drag_offset, bool hide_on_escape); @@ -193,10 +194,6 @@ class VIEWS_EXPORT HWNDMessageHandler : void SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon); - void set_remove_standard_frame(bool remove_standard_frame) { - remove_standard_frame_ = remove_standard_frame; - } - void set_use_system_default_icon(bool use_system_default_icon) { use_system_default_icon_ = use_system_default_icon; } @@ -206,6 +203,10 @@ class VIEWS_EXPORT HWNDMessageHandler : // Updates the window style to reflect whether it can be resized or maximized. void SizeConstraintsChanged(); + // Returns true if content is rendered to a child window instead of directly + // to this window. + bool HasChildRenderingWindow(); + private: typedef std::set<DWORD> TouchIDs; @@ -255,7 +256,10 @@ class VIEWS_EXPORT HWNDMessageHandler : // Called after the WM_ACTIVATE message has been processed by the default // windows procedure. - void PostProcessActivateMessage(int activation_state, bool minimized); + void PostProcessActivateMessage( + int activation_state, + bool minimized, + HWND window_gaining_or_losing_activation); // Enables disabled owner windows that may have been disabled due to this // window's modality. @@ -306,6 +310,8 @@ class VIEWS_EXPORT HWNDMessageHandler : // onscreen. void ForceRedrawWindow(int attempts); + bool HasSystemFrame() const; + // Message Handlers ---------------------------------------------------------- BEGIN_SAFE_MSG_MAP_EX(weak_factory_) @@ -502,6 +508,11 @@ class VIEWS_EXPORT HWNDMessageHandler : WPARAM w_param, LPARAM l_param); + // Helper function for setting the bounds of the HWND. For more information + // please refer to the SetBounds() function. + void SetBoundsInternal(const gfx::Rect& bounds_in_pixels, + bool force_size_changed); + HWNDMessageHandlerDelegate* delegate_; scoped_ptr<FullscreenHandler> fullscreen_handler_; @@ -509,8 +520,6 @@ class VIEWS_EXPORT HWNDMessageHandler : // Set to true in Close() and false is CloseNow(). bool waiting_for_close_now_; - bool remove_standard_frame_; - bool use_system_default_icon_; // Whether all ancestors have been enabled. This is only used if is_modal_ is @@ -632,6 +641,10 @@ class VIEWS_EXPORT HWNDMessageHandler : // Defaults to false. bool left_button_down_on_caption_; + // Set to true if the window is a background fullscreen window, i.e a + // fullscreen window which lost activation. Defaults to false. + bool background_fullscreen_hack_; + // The WeakPtrFactories below must occur last in the class definition so they // get destroyed last. diff --git a/chromium/ui/views/win/hwnd_message_handler_delegate.h b/chromium/ui/views/win/hwnd_message_handler_delegate.h index 55ac4407197..007157aefe8 100644 --- a/chromium/ui/views/win/hwnd_message_handler_delegate.h +++ b/chromium/ui/views/win/hwnd_message_handler_delegate.h @@ -24,21 +24,33 @@ class TouchEvent; namespace views { +enum class FrameMode { + SYSTEM_DRAWN, // "glass" frame + CUSTOM_DRAWN // "opaque" frame +}; + class InputMethod; // Implemented by the object that uses the HWNDMessageHandler to handle // notifications from the underlying HWND and service requests for data. class VIEWS_EXPORT HWNDMessageHandlerDelegate { public: - virtual bool IsWidgetWindow() const = 0; + // True if the widget associated with this window has a non-client view. + virtual bool HasNonClientView() const = 0; + + // Returns who we want to be drawing the frame. Either the system (Windows) + // will handle it or Chrome will custom draw it. + virtual FrameMode GetFrameMode() const = 0; - // TODO(beng): resolve this more satisfactorily vis-a-vis ShouldUseNativeFrame - // to avoid confusion. - virtual bool IsUsingCustomFrame() const = 0; + // True if a frame should be drawn. This will return true for some windows + // that don't have a visible frame. Those usually have the WS_POPUP style, for + // which Windows will remove the frame automatically if the frame mode is + // SYSTEM_DRAWN. + virtual bool HasFrame() const = 0; virtual void SchedulePaint() = 0; - virtual void EnableInactiveRendering() = 0; - virtual bool IsInactiveRenderingDisabled() = 0; + virtual void SetAlwaysRenderAsActive(bool always_render_as_active) = 0; + virtual bool IsAlwaysRenderAsActive() = 0; virtual bool CanResize() const = 0; virtual bool CanMaximize() const = 0; diff --git a/chromium/ui/views/win/windows_session_change_observer.cc b/chromium/ui/views/win/windows_session_change_observer.cc index a96c566517b..6d230d6edd0 100644 --- a/chromium/ui/views/win/windows_session_change_observer.cc +++ b/chromium/ui/views/win/windows_session_change_observer.cc @@ -5,7 +5,6 @@ #include "ui/views/win/windows_session_change_observer.h" #include <wtsapi32.h> -#pragma comment(lib, "wtsapi32.lib") #include "base/bind.h" #include "base/bind_helpers.h" diff --git a/chromium/ui/views/window/client_view.cc b/chromium/ui/views/window/client_view.cc index fb7d89fed18..7a87d8decde 100644 --- a/chromium/ui/views/window/client_view.cc +++ b/chromium/ui/views/window/client_view.cc @@ -92,6 +92,8 @@ void ClientView::ViewHierarchyChanged( // Insert |contents_view_| at index 0 so it is first in the focus chain. // (the OK/Cancel buttons are inserted before contents_view_) AddChildViewAt(contents_view_, 0); + } else if (!details.is_add && details.child == contents_view_) { + contents_view_ = nullptr; } } diff --git a/chromium/ui/views/window/custom_frame_view.cc b/chromium/ui/views/window/custom_frame_view.cc index 05cb09d0780..720091ec624 100644 --- a/chromium/ui/views/window/custom_frame_view.cc +++ b/chromium/ui/views/window/custom_frame_view.cc @@ -352,9 +352,9 @@ bool CustomFrameView::ShouldShowClientEdge() const { void CustomFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { frame_background_->set_frame_color(GetFrameColor()); - const gfx::ImageSkia* frame_image = GetFrameImage(); + const gfx::ImageSkia frame_image = GetFrameImage(); frame_background_->set_theme_image(frame_image); - frame_background_->set_top_area_height(frame_image->height()); + frame_background_->set_top_area_height(frame_image.height()); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); @@ -373,9 +373,9 @@ void CustomFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { } void CustomFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { - const gfx::ImageSkia* frame_image = GetFrameImage(); + const gfx::ImageSkia frame_image = GetFrameImage(); frame_background_->set_theme_image(frame_image); - frame_background_->set_top_area_height(frame_image->height()); + frame_background_->set_top_area_height(frame_image.height()); frame_background_->PaintMaximized(canvas, this); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); @@ -475,9 +475,11 @@ SkColor CustomFrameView::GetFrameColor() const { return frame_->IsActive() ? kDefaultColorFrame : kDefaultColorFrameInactive; } -const gfx::ImageSkia* CustomFrameView::GetFrameImage() const { - return ui::ResourceBundle::GetSharedInstance().GetImageNamed( - frame_->IsActive() ? IDR_FRAME : IDR_FRAME_INACTIVE).ToImageSkia(); +gfx::ImageSkia CustomFrameView::GetFrameImage() const { + return *ui::ResourceBundle::GetSharedInstance() + .GetImageNamed(frame_->IsActive() ? IDR_FRAME + : IDR_FRAME_INACTIVE) + .ToImageSkia(); } void CustomFrameView::LayoutWindowControls() { diff --git a/chromium/ui/views/window/custom_frame_view.h b/chromium/ui/views/window/custom_frame_view.h index cb2413125f0..db77648a273 100644 --- a/chromium/ui/views/window/custom_frame_view.h +++ b/chromium/ui/views/window/custom_frame_view.h @@ -8,14 +8,11 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "ui/gfx/image/image_skia.h" #include "ui/views/controls/button/button.h" #include "ui/views/window/frame_buttons.h" #include "ui/views/window/non_client_view.h" -namespace gfx { -class ImageSkia; -} - namespace views { class FrameBackground; @@ -105,7 +102,7 @@ class VIEWS_EXPORT CustomFrameView : public NonClientFrameView, // Compute aspects of the frame needed to paint the frame background. SkColor GetFrameColor() const; - const gfx::ImageSkia* GetFrameImage() const; + gfx::ImageSkia GetFrameImage() const; // Performs the layout for the window control buttons based on the // configuration specified in WindowButtonOrderProvider. The sizing and diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc index 04f9b2fc4da..5b65adb73bd 100644 --- a/chromium/ui/views/window/dialog_client_view.cc +++ b/chromium/ui/views/window/dialog_client_view.cc @@ -54,50 +54,48 @@ void LayoutButton(LabelButton* button, gfx::Rect* row_bounds) { DialogClientView::DialogClientView(Widget* owner, View* contents_view) : ClientView(owner, contents_view), + button_row_insets_(0, + kButtonHEdgeMarginNew, + kButtonVEdgeMarginNew, + kButtonHEdgeMarginNew), ok_button_(NULL), cancel_button_(NULL), - default_button_(NULL), - focus_manager_(NULL), extra_view_(NULL), - footnote_view_(NULL), - notified_delegate_(false) { + delegate_allowed_close_(false) { + // Doing this now ensures this accelerator will have lower priority than + // one set by the contents view. + AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); } DialogClientView::~DialogClientView() { } void DialogClientView::AcceptWindow() { - // Only notify the delegate once. See |notified_delegate_|'s comment. - if (!notified_delegate_ && GetDialogDelegate()->Accept(false)) { - notified_delegate_ = true; - Close(); + // Only notify the delegate once. See |delegate_allowed_close_|'s comment. + if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) { + delegate_allowed_close_ = true; + GetWidget()->Close(); } } void DialogClientView::CancelWindow() { - // Only notify the delegate once. See |notified_delegate_|'s comment. - if (!notified_delegate_ && GetDialogDelegate()->Cancel()) { - notified_delegate_ = true; - Close(); + // Only notify the delegate once. See |delegate_allowed_close_|'s comment. + if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) { + delegate_allowed_close_ = true; + GetWidget()->Close(); } } void DialogClientView::UpdateDialogButtons() { const int buttons = GetDialogDelegate()->GetDialogButtons(); - ui::Accelerator escape(ui::VKEY_ESCAPE, ui::EF_NONE); - if (default_button_) - default_button_->SetIsDefault(false); - default_button_ = NULL; if (buttons & ui::DIALOG_BUTTON_OK) { if (!ok_button_) { ok_button_ = CreateDialogButton(ui::DIALOG_BUTTON_OK); - if (!(buttons & ui::DIALOG_BUTTON_CANCEL)) - ok_button_->AddAccelerator(escape); AddChildView(ok_button_); } - UpdateButton(ok_button_, ui::DIALOG_BUTTON_OK); + GetDialogDelegate()->UpdateButton(ok_button_, ui::DIALOG_BUTTON_OK); } else if (ok_button_) { delete ok_button_; ok_button_ = NULL; @@ -106,38 +104,27 @@ void DialogClientView::UpdateDialogButtons() { if (buttons & ui::DIALOG_BUTTON_CANCEL) { if (!cancel_button_) { cancel_button_ = CreateDialogButton(ui::DIALOG_BUTTON_CANCEL); - cancel_button_->AddAccelerator(escape); AddChildView(cancel_button_); } - UpdateButton(cancel_button_, ui::DIALOG_BUTTON_CANCEL); + GetDialogDelegate()->UpdateButton(cancel_button_, ui::DIALOG_BUTTON_CANCEL); } else if (cancel_button_) { delete cancel_button_; cancel_button_ = NULL; } - // Use the escape key to close the window if there are no dialog buttons. - if (!has_dialog_buttons()) - AddAccelerator(escape); - else - ResetAccelerators(); + SetupFocusChain(); } /////////////////////////////////////////////////////////////////////////////// // DialogClientView, ClientView overrides: bool DialogClientView::CanClose() { - if (notified_delegate_) - return true; - - // The dialog is closing but no Accept or Cancel action has been performed - // before: it's a Close action. - if (GetDialogDelegate()->Close()) { - notified_delegate_ = true; - GetDialogDelegate()->OnClosed(); - return true; - } - return false; + // If the dialog is closing but no Accept or Cancel action has been performed + // before, it's a Close action. + if (!delegate_allowed_close_) + delegate_allowed_close_ = GetDialogDelegate()->Close(); + return delegate_allowed_close_; } DialogClientView* DialogClientView::AsDialogClientView() { @@ -148,31 +135,6 @@ const DialogClientView* DialogClientView::AsDialogClientView() const { return this; } -void DialogClientView::OnWillChangeFocus(View* focused_before, - View* focused_now) { - // Make the newly focused button default or restore the dialog's default. - const int default_button = GetDialogDelegate()->GetDefaultDialogButton(); - LabelButton* new_default_button = NULL; - if (focused_now && - !strcmp(focused_now->GetClassName(), LabelButton::kViewClassName)) { - new_default_button = static_cast<LabelButton*>(focused_now); - } else if (default_button == ui::DIALOG_BUTTON_OK && ok_button_) { - new_default_button = ok_button_; - } else if (default_button == ui::DIALOG_BUTTON_CANCEL && cancel_button_) { - new_default_button = cancel_button_; - } - - if (default_button_ && default_button_ != new_default_button) - default_button_->SetIsDefault(false); - default_button_ = new_default_button; - if (default_button_ && !default_button_->is_default()) - default_button_->SetIsDefault(true); -} - -void DialogClientView::OnDidChangeFocus(View* focused_before, - View* focused_now) { -} - //////////////////////////////////////////////////////////////////////////////// // DialogClientView, View overrides: @@ -204,31 +166,12 @@ gfx::Size DialogClientView::GetPreferredSize() const { size.Enlarge(0, contents_size.height()); size.set_width(std::max(size.width(), contents_size.width())); - // Increase the size as needed to fit the footnote view. - if (ShouldShow(footnote_view_)) { - gfx::Size footnote_size = footnote_view_->GetPreferredSize(); - if (!footnote_size.IsEmpty()) - size.set_width(std::max(size.width(), footnote_size.width())); - - int footnote_height = footnote_view_->GetHeightForWidth(size.width()); - size.Enlarge(0, footnote_height); - } - return size; } void DialogClientView::Layout() { gfx::Rect bounds = GetContentsBounds(); - // Layout the footnote view. - if (ShouldShow(footnote_view_)) { - const int height = footnote_view_->GetHeightForWidth(bounds.width()); - footnote_view_->SetBounds(bounds.x(), bounds.bottom() - height, - bounds.width(), height); - if (height != 0) - bounds.Inset(0, 0, 0, height); - } - // Layout the row containing the buttons and the extra view. if (has_dialog_buttons() || ShouldShow(extra_view_)) { bounds.Inset(GetButtonRowInsets()); @@ -269,7 +212,12 @@ void DialogClientView::Layout() { bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) { DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE); - Close(); + + // If there's a cancel button, it handles escape. + if (cancel_button_) + return cancel_button_->AcceleratorPressed(accelerator); + + GetWidget()->Close(); return true; } @@ -277,35 +225,15 @@ void DialogClientView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { ClientView::ViewHierarchyChanged(details); if (details.is_add && details.child == this) { - focus_manager_ = GetFocusManager(); - if (focus_manager_) - GetFocusManager()->AddFocusChangeListener(this); - UpdateDialogButtons(); CreateExtraView(); - CreateFootnoteView(); - } else if (!details.is_add && details.child == this) { - if (focus_manager_) - focus_manager_->RemoveFocusChangeListener(this); - focus_manager_ = NULL; - } else if (!details.is_add) { - if (details.child == default_button_) - default_button_ = NULL; + } else if (!details.is_add && details.child != this) { if (details.child == ok_button_) - ok_button_ = NULL; - if (details.child == cancel_button_) - cancel_button_ = NULL; - } -} - -void DialogClientView::NativeViewHierarchyChanged() { - FocusManager* focus_manager = GetFocusManager(); - if (focus_manager_ != focus_manager) { - if (focus_manager_) - focus_manager_->RemoveFocusChangeListener(this); - focus_manager_ = focus_manager; - if (focus_manager_) - focus_manager_->AddFocusChangeListener(this); + ok_button_ = nullptr; + else if (details.child == cancel_button_) + cancel_button_ = nullptr; + else if (details.child == extra_view_) + extra_view_ = nullptr; } } @@ -343,11 +271,8 @@ DialogClientView::DialogClientView(View* contents_view) : ClientView(NULL, contents_view), ok_button_(NULL), cancel_button_(NULL), - default_button_(NULL), - focus_manager_(NULL), extra_view_(NULL), - footnote_view_(NULL), - notified_delegate_(false) {} + delegate_allowed_close_(false) {} DialogDelegate* DialogClientView::GetDialogDelegate() const { return GetWidget()->widget_delegate()->AsDialogDelegate(); @@ -361,20 +286,12 @@ void DialogClientView::CreateExtraView() { if (extra_view_) { extra_view_->SetGroup(kButtonGroup); AddChildView(extra_view_); + SetupFocusChain(); } } -void DialogClientView::CreateFootnoteView() { - if (footnote_view_) - return; - - footnote_view_ = GetDialogDelegate()->CreateFootnoteView(); - if (footnote_view_) - AddChildView(footnote_view_); -} - void DialogClientView::ChildPreferredSizeChanged(View* child) { - if (child == footnote_view_ || child == extra_view_) + if (child == extra_view_) Layout(); } @@ -404,18 +321,6 @@ LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { return button; } -void DialogClientView::UpdateButton(LabelButton* button, - ui::DialogButton type) { - DialogDelegate* dialog = GetDialogDelegate(); - button->SetText(dialog->GetDialogButtonLabel(type)); - button->SetEnabled(dialog->IsDialogButtonEnabled(type)); - - if (type == dialog->GetDefaultDialogButton()) { - default_button_ = button; - button->SetIsDefault(true); - } -} - int DialogClientView::GetButtonsAndExtraViewRowHeight() const { int extra_view_height = ShouldShow(extra_view_) ? extra_view_->GetPreferredSize().height() : 0; @@ -426,15 +331,32 @@ int DialogClientView::GetButtonsAndExtraViewRowHeight() const { } gfx::Insets DialogClientView::GetButtonRowInsets() const { - // NOTE: The insets only apply to the buttons, extra view, and footnote view. - return GetButtonsAndExtraViewRowHeight() == 0 ? gfx::Insets() : - gfx::Insets(0, kButtonHEdgeMarginNew, - kButtonVEdgeMarginNew, kButtonHEdgeMarginNew); + return GetButtonsAndExtraViewRowHeight() == 0 ? gfx::Insets() + : button_row_insets_; } -void DialogClientView::Close() { - GetWidget()->Close(); - GetDialogDelegate()->OnClosed(); +void DialogClientView::SetupFocusChain() { + // Create a vector of child views in the order of intended focus. + std::vector<View*> child_views; + child_views.push_back(contents_view()); + child_views.push_back(extra_view_); + if (kIsOkButtonOnLeftSide) { + child_views.push_back(ok_button_); + child_views.push_back(cancel_button_); + } else { + child_views.push_back(cancel_button_); + child_views.push_back(ok_button_); + } + + // Remove all null views from the vector. + child_views.erase( + std::remove(child_views.begin(), child_views.end(), nullptr), + child_views.end()); + + // Setup focus by reordering views. It is not safe to use SetNextFocusableView + // since child views may be added externally to this view. + for (size_t i = 0; i < child_views.size(); i++) + ReorderChildView(child_views[i], i); } } // namespace views diff --git a/chromium/ui/views/window/dialog_client_view.h b/chromium/ui/views/window/dialog_client_view.h index 05e14b433ac..60080b00c8c 100644 --- a/chromium/ui/views/window/dialog_client_view.h +++ b/chromium/ui/views/window/dialog_client_view.h @@ -9,7 +9,6 @@ #include "base/macros.h" #include "ui/base/ui_base_types.h" #include "ui/views/controls/button/button.h" -#include "ui/views/focus/focus_manager.h" #include "ui/views/window/client_view.h" namespace views { @@ -20,18 +19,16 @@ class Widget; // DialogClientView provides adornments for a dialog's content view, including // custom-labeled [OK] and [Cancel] buttons with [Enter] and [Esc] accelerators. -// The view also displays the delegate's extra view alongside the buttons and -// the delegate's footnote view below the buttons. The view appears like below. -// NOTE: The contents view is not inset on the top or side client view edges. +// The view also displays the delegate's extra view alongside the buttons. The +// view appears like below. NOTE: The contents view is not inset on the top or +// side client view edges. // +------------------------------+ // | Contents View | // +------------------------------+ // | [Extra View] [OK] [Cancel] | -// | [ Footnote View ] | // +------------------------------+ class VIEWS_EXPORT DialogClientView : public ClientView, - public ButtonListener, - public FocusChangeListener { + public ButtonListener { public: DialogClientView(Widget* widget, View* contents_view); ~DialogClientView() override; @@ -52,22 +49,21 @@ class VIEWS_EXPORT DialogClientView : public ClientView, DialogClientView* AsDialogClientView() override; const DialogClientView* AsDialogClientView() const override; - // FocusChangeListener implementation: - void OnWillChangeFocus(View* focused_before, View* focused_now) override; - void OnDidChangeFocus(View* focused_before, View* focused_now) override; - // View implementation: gfx::Size GetPreferredSize() const override; void Layout() override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; - void NativeViewHierarchyChanged() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; // ButtonListener implementation: void ButtonPressed(Button* sender, const ui::Event& event) override; + void set_button_row_insets(const gfx::Insets& insets) { + button_row_insets_ = insets; + } + protected: // For testing. explicit DialogClientView(View* contents_view); @@ -78,16 +74,11 @@ class VIEWS_EXPORT DialogClientView : public ClientView, // Create and add the extra view, if supplied by the delegate. void CreateExtraView(); - // Creates and adds the footnote view, if supplied by the delegate. - void CreateFootnoteView(); - // View implementation. void ChildPreferredSizeChanged(View* child) override; void ChildVisibilityChanged(View* child) override; private: - FRIEND_TEST_ALL_PREFIXES(DialogClientViewTest, FocusManager); - bool has_dialog_buttons() const { return ok_button_ || cancel_button_; } // Create a dialog button of the appropriate type. @@ -102,30 +93,25 @@ class VIEWS_EXPORT DialogClientView : public ClientView, // Returns the insets for the buttons and extra view. gfx::Insets GetButtonRowInsets() const; - // Closes the widget. - void Close(); + // How much to inset the button row. + gfx::Insets button_row_insets_; + + // Sets up the focus chain for the child views. This is required since the + // delegate may choose to add/remove views at any time. + void SetupFocusChain(); // The dialog buttons. LabelButton* ok_button_; LabelButton* cancel_button_; - // The button that is currently default; may be NULL. - LabelButton* default_button_; - - // Observe |focus_manager_| to update the default button with focus changes. - FocusManager* focus_manager_; - // The extra view shown in the row of buttons; may be NULL. View* extra_view_; - // The footnote view shown below the buttons; may be NULL. - View* footnote_view_; - // True if we've notified the delegate the window is closing and the delegate - // allosed the close. In some situations it's possible to get two closes (see + // allowed the close. In some situations it's possible to get two closes (see // http://crbug.com/71940). This is used to avoid notifying the delegate // twice, which can have bad consequences. - bool notified_delegate_; + bool delegate_allowed_close_; DISALLOW_COPY_AND_ASSIGN(DialogClientView); }; diff --git a/chromium/ui/views/window/dialog_client_view_unittest.cc b/chromium/ui/views/window/dialog_client_view_unittest.cc index 2a08f3e684f..9078aba9f17 100644 --- a/chromium/ui/views/window/dialog_client_view_unittest.cc +++ b/chromium/ui/views/window/dialog_client_view_unittest.cc @@ -17,55 +17,55 @@ namespace views { class TestDialogClientView : public DialogClientView { public: - TestDialogClientView(View* contents_view, - DialogDelegate* dialog_delegate) - : DialogClientView(contents_view), - dialog_(dialog_delegate) {} + explicit TestDialogClientView(DialogDelegateView* dialog_delegate_view) + : DialogClientView(dialog_delegate_view), + dialog_delegate_view_(dialog_delegate_view) {} ~TestDialogClientView() override {} // DialogClientView implementation. - DialogDelegate* GetDialogDelegate() const override { return dialog_; } + DialogDelegate* GetDialogDelegate() const override { + return dialog_delegate_view_; + } View* GetContentsView() { return contents_view(); } void CreateExtraViews() { CreateExtraView(); - CreateFootnoteView(); } private: - DialogDelegate* dialog_; + DialogDelegateView* dialog_delegate_view_; DISALLOW_COPY_AND_ASSIGN(TestDialogClientView); }; +// Base class for tests. Also acts as the dialog delegate and contents view for +// TestDialogClientView. class DialogClientViewTest : public ViewsTestBase, public DialogDelegateView { public: DialogClientViewTest() : dialog_buttons_(ui::DIALOG_BUTTON_NONE), - extra_view_(NULL), - footnote_view_(NULL) {} + extra_view_(nullptr) {} ~DialogClientViewTest() override {} // testing::Test implementation. void SetUp() override { dialog_buttons_ = ui::DIALOG_BUTTON_NONE; - contents_.reset(new StaticSizedView(gfx::Size(100, 200))); - client_view_.reset(new TestDialogClientView(contents_.get(), this)); - + client_view_.reset(new TestDialogClientView(this)); + // Add this i.e. the contents view as a child of |client_view_|. This is + // generally done when the client view is added to the view hierarchy. + client_view_->AddChildViewAt(this, 0); ViewsTestBase::SetUp(); } // DialogDelegateView implementation. - View* GetContentsView() override { return contents_.get(); } View* CreateExtraView() override { return extra_view_; } bool GetExtraViewPadding(int* padding) override { if (extra_view_padding_) *padding = *extra_view_padding_; return extra_view_padding_.get() != nullptr; } - View* CreateFootnoteView() override { return footnote_view_; } int GetDialogButtons() const override { return dialog_buttons_; } protected: @@ -79,11 +79,11 @@ class DialogClientViewTest : public ViewsTestBase, // the requested amount, but height should always match exactly. void CheckContentsIsSetToPreferredSize() { const gfx::Rect client_bounds = GetUpdatedClientBounds(); - const gfx::Size preferred_size = contents_->GetPreferredSize(); - EXPECT_EQ(preferred_size.height(), contents_->bounds().height()); - EXPECT_LE(preferred_size.width(), contents_->bounds().width()); - EXPECT_EQ(contents_->bounds().origin(), client_bounds.origin()); - EXPECT_EQ(contents_->bounds().right(), client_bounds.right()); + const gfx::Size preferred_size = this->GetPreferredSize(); + EXPECT_EQ(preferred_size.height(), this->bounds().height()); + EXPECT_LE(preferred_size.width(), this->bounds().width()); + EXPECT_EQ(this->bounds().origin(), client_bounds.origin()); + EXPECT_EQ(this->bounds().right(), client_bounds.right()); } // Sets the buttons to show in the dialog and refreshes the dialog. @@ -106,25 +106,15 @@ class DialogClientViewTest : public ViewsTestBase, client_view_->Layout(); } - // Sets the footnote view. - void SetFootnoteView(View* view) { - DCHECK(!footnote_view_); - footnote_view_ = view; - client_view_->CreateExtraViews(); - } - TestDialogClientView* client_view() { return client_view_.get(); } private: - // The contents of the dialog. - scoped_ptr<View> contents_; // The DialogClientView that's being tested. scoped_ptr<TestDialogClientView> client_view_; // The bitmask of buttons to show in the dialog. int dialog_buttons_; View* extra_view_; // weak scoped_ptr<int> extra_view_padding_; // Null by default. - View* footnote_view_; // weak DISALLOW_COPY_AND_ASSIGN(DialogClientViewTest); }; @@ -176,6 +166,50 @@ TEST_F(DialogClientViewTest, RemoveAndUpdateButtons) { EXPECT_FALSE(client_view()->cancel_button()->is_default()); } +// Test that views inside the dialog client view have the correct focus order. +TEST_F(DialogClientViewTest, SetupFocusChain) { +#if defined(OS_WIN) || defined(OS_CHROMEOS) + const bool kIsOkButtonOnLeftSide = true; +#else + const bool kIsOkButtonOnLeftSide = false; +#endif + + // Initially the dialog client view only contains the content view. + EXPECT_EQ(nullptr, client_view()->GetContentsView()->GetNextFocusableView()); + + // Add OK and cancel buttons. + SetDialogButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); + + if (kIsOkButtonOnLeftSide) { + EXPECT_EQ(client_view()->ok_button(), + client_view()->GetContentsView()->GetNextFocusableView()); + EXPECT_EQ(client_view()->cancel_button(), + client_view()->ok_button()->GetNextFocusableView()); + EXPECT_EQ(nullptr, client_view()->cancel_button()->GetNextFocusableView()); + } else { + EXPECT_EQ(client_view()->cancel_button(), + client_view()->GetContentsView()->GetNextFocusableView()); + EXPECT_EQ(client_view()->ok_button(), + client_view()->cancel_button()->GetNextFocusableView()); + EXPECT_EQ(nullptr, client_view()->ok_button()->GetNextFocusableView()); + } + + // Add extra view and remove OK button. + View* extra_view = new StaticSizedView(gfx::Size(200, 200)); + SetExtraView(extra_view); + SetDialogButtons(ui::DIALOG_BUTTON_CANCEL); + + EXPECT_EQ(extra_view, + client_view()->GetContentsView()->GetNextFocusableView()); + EXPECT_EQ(client_view()->cancel_button(), extra_view->GetNextFocusableView()); + EXPECT_EQ(nullptr, client_view()->cancel_button()->GetNextFocusableView()); + + // Add a dummy view to the client view. + View* dummy_view = new StaticSizedView(gfx::Size(200, 200)); + client_view()->AddChildView(dummy_view); + EXPECT_EQ(dummy_view, client_view()->cancel_button()->GetNextFocusableView()); +} + // Test that the contents view gets its preferred size in the basic dialog // configuration. TEST_F(DialogClientViewTest, ContentsSize) { @@ -217,103 +251,4 @@ TEST_F(DialogClientViewTest, LayoutWithButtons) { EXPECT_GT(width_of_extra_view, extra_view->bounds().width()); } -// Test the effect of the footnote view on layout. -TEST_F(DialogClientViewTest, LayoutWithFootnote) { - CheckContentsIsSetToPreferredSize(); - gfx::Size no_footnote_size = client_view()->bounds().size(); - - View* footnote_view = new StaticSizedView(gfx::Size(200, 200)); - SetFootnoteView(footnote_view); - CheckContentsIsSetToPreferredSize(); - EXPECT_GT(client_view()->bounds().height(), no_footnote_size.height()); - EXPECT_EQ(200, footnote_view->bounds().height()); - gfx::Size with_footnote_size = client_view()->bounds().size(); - EXPECT_EQ(with_footnote_size.width(), footnote_view->bounds().width()); - - SetDialogButtons(ui::DIALOG_BUTTON_CANCEL); - CheckContentsIsSetToPreferredSize(); - EXPECT_LE(with_footnote_size.height(), client_view()->bounds().height()); - EXPECT_LE(with_footnote_size.width(), client_view()->bounds().width()); - gfx::Size with_footnote_and_button_size = client_view()->bounds().size(); - - SetDialogButtons(ui::DIALOG_BUTTON_NONE); - footnote_view->SetVisible(false); - CheckContentsIsSetToPreferredSize(); - EXPECT_EQ(no_footnote_size.height(), client_view()->bounds().height()); - EXPECT_EQ(no_footnote_size.width(), client_view()->bounds().width()); -} - -// Test that GetHeightForWidth is respected for the footnote view. -TEST_F(DialogClientViewTest, LayoutWithFootnoteHeightForWidth) { - CheckContentsIsSetToPreferredSize(); - gfx::Size no_footnote_size = client_view()->bounds().size(); - - View* footnote_view = new ProportionallySizedView(3); - SetFootnoteView(footnote_view); - CheckContentsIsSetToPreferredSize(); - EXPECT_GT(client_view()->bounds().height(), no_footnote_size.height()); - EXPECT_EQ(footnote_view->bounds().width() * 3, - footnote_view->bounds().height()); -} - -// No ReparentNativeView on Mac. See http://crbug.com/514920. -#if defined(OS_MACOSX) && !defined(USE_AURA) -#define MAYBE_FocusManager DISABLED_FocusManager -#else -#define MAYBE_FocusManager FocusManager -#endif - -// Test that the DialogClientView's FocusManager is properly updated when the -// DialogClientView belongs to a non top level widget and the widget is -// reparented. The DialogClientView belongs to a non top level widget in the -// case of constrained windows. The constrained window's widget is reparented -// when a browser tab is dragged to a different browser window. -TEST_F(DialogClientViewTest, MAYBE_FocusManager) { - scoped_ptr<Widget> toplevel1(new Widget); - Widget::InitParams toplevel1_params = - CreateParams(Widget::InitParams::TYPE_WINDOW); - toplevel1_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - toplevel1->Init(toplevel1_params); - - scoped_ptr<Widget> toplevel2(new Widget); - Widget::InitParams toplevel2_params = - CreateParams(Widget::InitParams::TYPE_WINDOW); - toplevel2_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - toplevel2->Init(toplevel2_params); - - Widget* dialog = new Widget; - Widget::InitParams dialog_params = - CreateParams(Widget::InitParams::TYPE_WINDOW); - dialog_params.child = true; - dialog_params.delegate = new DialogDelegateView(); - dialog_params.parent = toplevel1->GetNativeView(); - dialog->Init(dialog_params); - - // Test that the FocusManager has been properly set when the DialogClientView - // was parented to |dialog|. - DialogClientView* client_view = - static_cast<DialogClientView*>(dialog->client_view()); - EXPECT_EQ(toplevel1->GetFocusManager(), client_view->focus_manager_); - - // Test that the FocusManager is properly updated when the DialogClientView's - // top level widget is changed. - Widget::ReparentNativeView(dialog->GetNativeView(), NULL); - EXPECT_EQ(NULL, client_view->focus_manager_); - Widget::ReparentNativeView(dialog->GetNativeView(), - toplevel2->GetNativeView()); - EXPECT_EQ(toplevel2->GetFocusManager(), client_view->focus_manager_); - Widget::ReparentNativeView(dialog->GetNativeView(), - toplevel1->GetNativeView()); - EXPECT_NE(toplevel1->GetFocusManager(), toplevel2->GetFocusManager()); - EXPECT_EQ(toplevel1->GetFocusManager(), client_view->focus_manager_); - - // Test that the FocusManager is properly cleared when the DialogClientView is - // removed from |dialog| during the widget's destruction. - client_view->set_owned_by_client(); - scoped_ptr<DialogClientView> owned_client_view(client_view); - toplevel1->CloseNow(); - toplevel2->CloseNow(); - EXPECT_EQ(NULL, owned_client_view->focus_manager_); -} - } // namespace views diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc index 8f292ecec82..84ed6b9fc2e 100644 --- a/chromium/ui/views/window/dialog_delegate.cc +++ b/chromium/ui/views/window/dialog_delegate.cc @@ -15,6 +15,7 @@ #include "ui/views/bubble/bubble_border.h" #include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/layout/layout_constants.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" #include "ui/views/window/dialog_client_view.h" @@ -28,11 +29,9 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // DialogDelegate: -DialogDelegate::DialogDelegate() : supports_new_style_(true) { -} +DialogDelegate::DialogDelegate() : supports_new_style_(true) {} -DialogDelegate::~DialogDelegate() { -} +DialogDelegate::~DialogDelegate() {} // static Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate, @@ -93,10 +92,6 @@ bool DialogDelegate::GetExtraViewPadding(int* padding) { return false; } -View* DialogDelegate::CreateTitlebarExtraView() { - return NULL; -} - View* DialogDelegate::CreateFootnoteView() { return NULL; } @@ -105,10 +100,6 @@ bool DialogDelegate::Cancel() { return true; } -bool DialogDelegate::Accept(bool window_closing) { - return Accept(); -} - bool DialogDelegate::Accept() { return true; } @@ -119,11 +110,13 @@ bool DialogDelegate::Close() { (buttons == ui::DIALOG_BUTTON_NONE)) { return Cancel(); } - return Accept(true); + return Accept(); } -base::string16 DialogDelegate::GetDialogTitle() const { - return GetWindowTitle(); +void DialogDelegate::UpdateButton(LabelButton* button, ui::DialogButton type) { + button->SetText(GetDialogButtonLabel(type)); + button->SetEnabled(IsDialogButtonEnabled(type)); + button->SetIsDefault(type == GetDefaultDialogButton()); } int DialogDelegate::GetDialogButtons() const { @@ -195,23 +188,18 @@ NonClientFrameView* DialogDelegate::CreateNonClientFrameView(Widget* widget) { // static NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) { - BubbleFrameView* frame = new BubbleFrameView(gfx::Insets()); -#if defined(OS_MACOSX) - // On Mac, dialogs have no border stroke and use a shadow provided by the OS. - const BubbleBorder::Shadow kShadow = BubbleBorder::NO_ASSETS; -#else + BubbleFrameView* frame = + new BubbleFrameView(gfx::Insets(kPanelVertMargin, kButtonHEdgeMarginNew, + 0, kButtonHEdgeMarginNew), + gfx::Insets()); const BubbleBorder::Shadow kShadow = BubbleBorder::SMALL_SHADOW; -#endif scoped_ptr<BubbleBorder> border( new BubbleBorder(BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor)); border->set_use_theme_background_color(true); frame->SetBubbleBorder(std::move(border)); DialogDelegate* delegate = widget->widget_delegate()->AsDialogDelegate(); - if (delegate) { - View* titlebar_view = delegate->CreateTitlebarExtraView(); - if (titlebar_view) - frame->SetTitlebarExtraView(titlebar_view); - } + if (delegate) + frame->SetFootnoteView(delegate->CreateFootnoteView()); return frame; } @@ -258,7 +246,7 @@ View* DialogDelegateView::GetContentsView() { } void DialogDelegateView::GetAccessibleState(ui::AXViewState* state) { - state->name = GetDialogTitle(); + state->name = GetWindowTitle(); state->role = ui::AX_ROLE_DIALOG; } diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h index dc9f89a1aac..06c9bbe6b93 100644 --- a/chromium/ui/views/window/dialog_delegate.h +++ b/chromium/ui/views/window/dialog_delegate.h @@ -16,6 +16,7 @@ namespace views { class DialogClientView; +class LabelButton; /////////////////////////////////////////////////////////////////////////////// // @@ -56,11 +57,6 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, // If a custom padding should be used, returns true and populates |padding|. virtual bool GetExtraViewPadding(int* padding); - // Override this function to display an extra view in the titlebar. - // Overrides may construct the view; this will only be called once per dialog. - // Note: this only works for new style dialogs. - virtual View* CreateTitlebarExtraView(); - // Override this function to display a footnote view below the buttons. // Overrides may construct the view; this will only be called once per dialog. virtual View* CreateFootnoteView(); @@ -76,10 +72,6 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, // or the Enter key. It can also be called on a close action if |Close| // has not been overridden. This function should return true if the window // can be closed after it returns, or false if it must remain open. - // If |window_closing| is true, it means that this handler is - // being called because the window is being closed (e.g. by Window::Close) - // and there is no Cancel handler, so Accept is being called instead. - virtual bool Accept(bool window_closing); virtual bool Accept(); // Called when the user closes the window without selecting an option, @@ -89,8 +81,12 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, // window can be closed after it returns, or false if it must remain open. virtual bool Close(); + // Updates the properties and appearance of |button| which has been created + // for type |type|. Override to do special initialization above and beyond + // the typical. + virtual void UpdateButton(LabelButton* button, ui::DialogButton type); + // Overridden from ui::DialogModel: - base::string16 GetDialogTitle() const override; int GetDialogButtons() const override; int GetDefaultDialogButton() const override; bool ShouldDefaultButtonBeBlue() const override; @@ -109,9 +105,6 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, // Returns whether this particular dialog should use the new dialog style. virtual bool UseNewStyleForThisDialog() const; - // Called when the window has been closed. - virtual void OnClosed() {} - // A helper for accessing the DialogClientView object contained by this // delegate's Window. const DialogClientView* GetDialogClientView() const; diff --git a/chromium/ui/views/window/dialog_delegate_unittest.cc b/chromium/ui/views/window/dialog_delegate_unittest.cc index 3499426b10f..86e957ddcb1 100644 --- a/chromium/ui/views/window/dialog_delegate_unittest.cc +++ b/chromium/ui/views/window/dialog_delegate_unittest.cc @@ -7,10 +7,12 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/hit_test.h" +#include "ui/events/event_processor.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/textfield/textfield.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_client_view.h" @@ -23,12 +25,24 @@ namespace { class TestDialog : public DialogDelegateView, public ButtonListener { public: TestDialog() - : canceled_(false), + : input_(new views::Textfield()), + canceled_(false), accepted_(false), closeable_(false), - last_pressed_button_(NULL) {} + last_pressed_button_(nullptr), + should_handle_escape_(false) { + AddChildView(input_); + } ~TestDialog() override {} + void Init() { + // Add the accelerator before being added to the widget hierarchy (before + // DCV has registered its accelerator) to make sure accelerator handling is + // not dependent on the order of AddAccelerator calls. + EXPECT_FALSE(GetWidget()); + AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); + } + // WidgetDelegate overrides: bool ShouldShowWindowTitle() const override { return !title_.empty(); @@ -46,7 +60,11 @@ class TestDialog : public DialogDelegateView, public ButtonListener { // DialogDelegateView overrides: gfx::Size GetPreferredSize() const override { return gfx::Size(200, 200); } + bool AcceleratorPressed(const ui::Accelerator& accelerator) override { + return should_handle_escape_; + } base::string16 GetWindowTitle() const override { return title_; } + View* GetInitiallyFocusedView() override { return input_; } bool UseNewStyleForThisDialog() const override { return true; } // ButtonListener override: @@ -56,25 +74,13 @@ class TestDialog : public DialogDelegateView, public ButtonListener { Button* last_pressed_button() const { return last_pressed_button_; } - void PressEnterAndCheckStates(Button* button) { - ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE); - GetFocusManager()->OnKeyEvent(key_event); - const DialogClientView* client_view = GetDialogClientView(); - EXPECT_EQ(canceled_, client_view->cancel_button()->is_default()); - EXPECT_EQ(accepted_, client_view->ok_button()->is_default()); - // This view does not listen for ok or cancel clicks, DialogClientView does. - CheckAndResetStates(button == client_view->cancel_button(), - button == client_view->ok_button(), - (canceled_ || accepted_ ) ? NULL : button); - } - void CheckAndResetStates(bool canceled, bool accepted, Button* last_pressed) { EXPECT_EQ(canceled, canceled_); canceled_ = false; EXPECT_EQ(accepted, accepted_); accepted_ = false; EXPECT_EQ(last_pressed, last_pressed_button_); - last_pressed_button_ = NULL; + last_pressed_button_ = nullptr; } void TearDown() { @@ -83,27 +89,35 @@ class TestDialog : public DialogDelegateView, public ButtonListener { } void set_title(const base::string16& title) { title_ = title; } + void set_should_handle_escape(bool should_handle_escape) { + should_handle_escape_ = should_handle_escape; + } + + views::Textfield* input() { return input_; } private: + views::Textfield* input_; bool canceled_; bool accepted_; // Prevent the dialog from closing, for repeated ok and cancel button clicks. bool closeable_; Button* last_pressed_button_; base::string16 title_; + bool should_handle_escape_; DISALLOW_COPY_AND_ASSIGN(TestDialog); }; class DialogTest : public ViewsTestBase { public: - DialogTest() : dialog_(NULL) {} + DialogTest() : dialog_(nullptr) {} ~DialogTest() override {} void SetUp() override { ViewsTestBase::SetUp(); dialog_ = new TestDialog(); - DialogDelegate::CreateDialogWidget(dialog_, GetContext(), NULL)->Show(); + dialog_->Init(); + DialogDelegate::CreateDialogWidget(dialog_, GetContext(), nullptr)->Show(); } void TearDown() override { @@ -111,6 +125,12 @@ class DialogTest : public ViewsTestBase { ViewsTestBase::TearDown(); } + void SimulateKeyEvent(const ui::KeyEvent& event) { + ui::KeyEvent event_copy = event; + if (dialog()->GetFocusManager()->OnKeyEvent(event_copy)) + dialog()->GetWidget()->OnKeyEvent(&event_copy); + } + TestDialog* dialog() const { return dialog_; } private: @@ -121,68 +141,38 @@ class DialogTest : public ViewsTestBase { } // namespace -TEST_F(DialogTest, DefaultButtons) { - DialogClientView* client_view = dialog()->GetDialogClientView(); - LabelButton* ok_button = client_view->ok_button(); - - // DialogDelegate's default button (ok) should be default (and handle enter). - EXPECT_EQ(ui::DIALOG_BUTTON_OK, dialog()->GetDefaultDialogButton()); - dialog()->PressEnterAndCheckStates(ok_button); - - // Focus another button in the dialog, it should become the default. - LabelButton* button_1 = new LabelButton(dialog(), base::string16()); - client_view->AddChildView(button_1); - client_view->OnWillChangeFocus(ok_button, button_1); - EXPECT_TRUE(button_1->is_default()); - dialog()->PressEnterAndCheckStates(button_1); - - // Focus a Checkbox (not a push button), OK should become the default again. - Checkbox* checkbox = new Checkbox(base::string16()); - client_view->AddChildView(checkbox); - client_view->OnWillChangeFocus(button_1, checkbox); - EXPECT_FALSE(button_1->is_default()); - dialog()->PressEnterAndCheckStates(ok_button); - - // Focus yet another button in the dialog, it should become the default. - LabelButton* button_2 = new LabelButton(dialog(), base::string16()); - client_view->AddChildView(button_2); - client_view->OnWillChangeFocus(checkbox, button_2); - EXPECT_FALSE(button_1->is_default()); - EXPECT_TRUE(button_2->is_default()); - dialog()->PressEnterAndCheckStates(button_2); - - // Focus nothing, OK should become the default again. - client_view->OnWillChangeFocus(button_2, NULL); - EXPECT_FALSE(button_1->is_default()); - EXPECT_FALSE(button_2->is_default()); - dialog()->PressEnterAndCheckStates(ok_button); -} - TEST_F(DialogTest, AcceptAndCancel) { DialogClientView* client_view = dialog()->GetDialogClientView(); LabelButton* ok_button = client_view->ok_button(); LabelButton* cancel_button = client_view->cancel_button(); // Check that return/escape accelerators accept/cancel dialogs. - const ui::KeyEvent return_key( + EXPECT_EQ(dialog()->input(), dialog()->GetFocusManager()->GetFocusedView()); + const ui::KeyEvent return_event( ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE); - dialog()->GetFocusManager()->OnKeyEvent(return_key); - dialog()->CheckAndResetStates(false, true, NULL); - const ui::KeyEvent escape_key( + SimulateKeyEvent(return_event); + dialog()->CheckAndResetStates(false, true, nullptr); + const ui::KeyEvent escape_event( ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, ui::EF_NONE); - dialog()->GetFocusManager()->OnKeyEvent(escape_key); - dialog()->CheckAndResetStates(true, false, NULL); + SimulateKeyEvent(escape_event); + dialog()->CheckAndResetStates(true, false, nullptr); // Check ok and cancel button behavior on a directed return key events. - ok_button->OnKeyPressed(return_key); - dialog()->CheckAndResetStates(false, true, NULL); - cancel_button->OnKeyPressed(return_key); - dialog()->CheckAndResetStates(true, false, NULL); + ok_button->OnKeyPressed(return_event); + dialog()->CheckAndResetStates(false, true, nullptr); + cancel_button->OnKeyPressed(return_event); + dialog()->CheckAndResetStates(true, false, nullptr); // Check that return accelerators cancel dialogs if cancel is focused. cancel_button->RequestFocus(); - dialog()->GetFocusManager()->OnKeyEvent(return_key); - dialog()->CheckAndResetStates(true, false, NULL); + EXPECT_EQ(cancel_button, dialog()->GetFocusManager()->GetFocusedView()); + SimulateKeyEvent(return_event); + dialog()->CheckAndResetStates(true, false, nullptr); + + // Check that escape can be overridden. + dialog()->set_should_handle_escape(true); + SimulateKeyEvent(escape_event); + dialog()->CheckAndResetStates(false, false, nullptr); } TEST_F(DialogTest, RemoveDefaultButton) { @@ -212,7 +202,8 @@ TEST_F(DialogTest, HitTest_HiddenTitle) { for (size_t i = 0; i < arraysize(cases); ++i) { gfx::Point point(cases[i].point, cases[i].point); EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point)) - << " with border: " << border << ", at point " << cases[i].point; + << " case " << i << " with border: " << border << ", at point " + << cases[i].point; } } @@ -246,7 +237,7 @@ TEST_F(DialogTest, HitTest_WithTitle) { TEST_F(DialogTest, BoundsAccommodateTitle) { TestDialog* dialog2(new TestDialog()); dialog2->set_title(base::ASCIIToUTF16("Title")); - DialogDelegate::CreateDialogWidget(dialog2, GetContext(), NULL); + DialogDelegate::CreateDialogWidget(dialog2, GetContext(), nullptr); // Titled dialogs have taller initial frame bounds than untitled dialogs. View* frame1 = dialog()->GetWidget()->non_client_view()->frame_view(); @@ -263,4 +254,10 @@ TEST_F(DialogTest, BoundsAccommodateTitle) { dialog2->TearDown(); } +// Tests default focus is assigned correctly when showing a new dialog. +TEST_F(DialogTest, InitialFocus) { + EXPECT_TRUE(dialog()->input()->HasFocus()); + EXPECT_EQ(dialog()->input(), dialog()->GetFocusManager()->GetFocusedView()); +} + } // namespace views diff --git a/chromium/ui/views/window/frame_background.cc b/chromium/ui/views/window/frame_background.cc index a325dbf9b3d..3e0e4ff5c06 100644 --- a/chromium/ui/views/window/frame_background.cc +++ b/chromium/ui/views/window/frame_background.cc @@ -13,20 +13,17 @@ namespace views { FrameBackground::FrameBackground() - : frame_color_(0), - theme_image_(NULL), - theme_overlay_image_(NULL), - top_area_height_(0), - left_edge_(NULL), - top_edge_(NULL), - right_edge_(NULL), - bottom_edge_(NULL), - top_left_corner_(NULL), - top_right_corner_(NULL), - bottom_left_corner_(NULL), - bottom_right_corner_(NULL), - maximized_top_inset_(0) { -} + : frame_color_(0), + top_area_height_(0), + left_edge_(nullptr), + top_edge_(nullptr), + right_edge_(nullptr), + bottom_edge_(nullptr), + top_left_corner_(nullptr), + top_right_corner_(nullptr), + bottom_left_corner_(nullptr), + bottom_right_corner_(nullptr), + maximized_top_inset_(0) {} FrameBackground::~FrameBackground() { } @@ -51,18 +48,19 @@ void FrameBackground::SetCornerImages(const gfx::ImageSkia* top_left, bottom_right_corner_ = bottom_right; } -void FrameBackground::PaintRestored(gfx::Canvas* canvas, View* view) const { +void FrameBackground::PaintRestored(gfx::Canvas* canvas, + const View* view) const { // Fill with the frame color first so we have a constant background for // areas not covered by the theme image. PaintFrameColor(canvas, view); - // Draw the theme frame. - canvas->TileImageInt(*theme_image_, - 0, 0, view->width(), theme_image_->height()); - - // Draw the theme frame overlay, if available. - if (theme_overlay_image_) - canvas->DrawImageInt(*theme_overlay_image_, 0, 0); + // Draw the theme frame and overlay, if available. + if (!theme_image_.isNull()) { + canvas->TileImageInt(theme_image_, 0, 0, view->width(), + theme_image_.height()); + } + if (!theme_overlay_image_.isNull()) + canvas->DrawImageInt(theme_overlay_image_, 0, 0); // Draw the top corners and edge, scaling the corner images down if they // are too big and relative to the vertical space available. @@ -119,29 +117,31 @@ void FrameBackground::PaintRestored(gfx::Canvas* canvas, View* view) const { left_edge_->width(), left_edge_height); } -void FrameBackground::PaintMaximized(gfx::Canvas* canvas, View* view) const { +void FrameBackground::PaintMaximized(gfx::Canvas* canvas, + const View* view) const { // We will be painting from -|maximized_top_inset_| to - // -|maximized_top_inset_| + |theme_image_|->height(). If this is less than + // -|maximized_top_inset_| + |theme_image_|.height(). If this is less than // |top_area_height_|, we need to paint the frame color to fill in the area // beneath the image. - int theme_frame_bottom = -maximized_top_inset_ + theme_image_->height(); + int theme_frame_bottom = -maximized_top_inset_ + + (theme_image_.isNull() ? 0 : theme_image_.height()); if (top_area_height_ > theme_frame_bottom) { canvas->FillRect(gfx::Rect(0, 0, view->width(), top_area_height_), frame_color_); } // Draw the theme frame. - canvas->TileImageInt(*theme_image_, - 0, - -maximized_top_inset_, - view->width(), - theme_image_->height()); + if (!theme_image_.isNull()) { + canvas->TileImageInt(theme_image_, 0, -maximized_top_inset_, view->width(), + theme_image_.height()); + } // Draw the theme frame overlay, if available. - if (theme_overlay_image_) - canvas->DrawImageInt(*theme_overlay_image_, 0, -maximized_top_inset_); + if (!theme_overlay_image_.isNull()) + canvas->DrawImageInt(theme_overlay_image_, 0, -maximized_top_inset_); } -void FrameBackground::PaintFrameColor(gfx::Canvas* canvas, View* view) const { +void FrameBackground::PaintFrameColor(gfx::Canvas* canvas, + const View* view) const { // Fill the top area. canvas->FillRect(gfx::Rect(0, 0, view->width(), top_area_height_), frame_color_); diff --git a/chromium/ui/views/window/frame_background.h b/chromium/ui/views/window/frame_background.h index c2278c10c96..a420de556cf 100644 --- a/chromium/ui/views/window/frame_background.h +++ b/chromium/ui/views/window/frame_background.h @@ -7,11 +7,11 @@ #include "base/macros.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/image/image_skia.h" #include "ui/views/views_export.h" namespace gfx { class Canvas; -class ImageSkia; } namespace views { @@ -29,14 +29,14 @@ class VIEWS_EXPORT FrameBackground { // Sets the color to draw under the frame images. void set_frame_color(SkColor color) { frame_color_ = color; } - // Sets the theme image for the top of the window. May be NULL. + // Sets the theme image for the top of the window. May be null (empty). // Memory is owned by the caller. - void set_theme_image(const gfx::ImageSkia* image) { theme_image_ = image; } + void set_theme_image(const gfx::ImageSkia& image) { theme_image_ = image; } // Sets an image that overlays the top window image. Usually used to add - // edge highlighting to provide the illusion of depth. May be NULL. + // edge highlighting to provide the illusion of depth. May be null (empty). // Memory is owned by the caller. - void set_theme_overlay_image(gfx::ImageSkia* image) { + void set_theme_overlay_image(const gfx::ImageSkia& image) { theme_overlay_image_ = image; } @@ -64,19 +64,19 @@ class VIEWS_EXPORT FrameBackground { // Paints the border for a standard, non-maximized window. Also paints the // background of the title bar area, since the top frame border and the // title bar background are a contiguous component. - void PaintRestored(gfx::Canvas* canvas, View* view) const; + void PaintRestored(gfx::Canvas* canvas, const View* view) const; // Paints the border for a maximized window, which does not include the // window edges. - void PaintMaximized(gfx::Canvas* canvas, View* view) const; + void PaintMaximized(gfx::Canvas* canvas, const View* view) const; private: // Fills the frame area with the frame color. - void PaintFrameColor(gfx::Canvas* canvas, View* view) const; + void PaintFrameColor(gfx::Canvas* canvas, const View* view) const; SkColor frame_color_; - const gfx::ImageSkia* theme_image_; - gfx::ImageSkia* theme_overlay_image_; + gfx::ImageSkia theme_image_; + gfx::ImageSkia theme_overlay_image_; int top_area_height_; // Images for the sides of the frame. diff --git a/chromium/ui/views/window/non_client_view.cc b/chromium/ui/views/window/non_client_view.cc index 7670a2eb5a2..a6d38a24f4c 100644 --- a/chromium/ui/views/window/non_client_view.cc +++ b/chromium/ui/views/window/non_client_view.cc @@ -31,11 +31,19 @@ static const int kClientViewIndex = 1; // The overlay view is always on top (index == child_count() - 1). //////////////////////////////////////////////////////////////////////////////// +// NonClientFrameView, default implementations: + +bool NonClientFrameView::GetClientMask(const gfx::Size& size, + gfx::Path* mask) const { + return false; +} + +//////////////////////////////////////////////////////////////////////////////// // NonClientView, public: NonClientView::NonClientView() - : client_view_(NULL), - overlay_view_(NULL) { + : client_view_(nullptr), + overlay_view_(nullptr) { SetEventTargeter( scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); } @@ -84,10 +92,6 @@ void NonClientView::UpdateFrame() { SchedulePaint(); } -void NonClientView::SetInactiveRenderingDisabled(bool disable) { - frame_view_->SetInactiveRenderingDisabled(disable); -} - gfx::Rect NonClientView::GetWindowBoundsForClientBounds( const gfx::Rect client_bounds) const { return frame_view_->GetWindowBoundsForClientBounds(client_bounds); @@ -163,6 +167,10 @@ void NonClientView::Layout() { // Then layout the ClientView, using those bounds. client_view_->SetBoundsRect(frame_view_->GetBoundsForClientView()); + gfx::Path client_clip; + if (frame_view_->GetClientMask(client_view_->size(), &client_clip)) + client_view_->set_clip_path(client_clip); + // We need to manually call Layout on the ClientView as well for the same // reason as above. client_view_->Layout(); @@ -244,20 +252,10 @@ View* NonClientView::TargetForRect(View* root, const gfx::Rect& rect) { NonClientFrameView::~NonClientFrameView() { } -void NonClientFrameView::SetInactiveRenderingDisabled(bool disable) { - if (inactive_rendering_disabled_ == disable) - return; - - bool should_paint_as_active_old = ShouldPaintAsActive(); - inactive_rendering_disabled_ = disable; - - // The widget schedules a paint when the activation changes. - if (should_paint_as_active_old != ShouldPaintAsActive()) - SchedulePaint(); -} - bool NonClientFrameView::ShouldPaintAsActive() const { - return inactive_rendering_disabled_ || GetWidget()->IsActive(); + return GetWidget()->IsAlwaysRenderAsActive() || + (active_state_override_ ? *active_state_override_ + : GetWidget()->IsActive()); } int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point, @@ -309,6 +307,9 @@ int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point, return can_resize ? component : HTBORDER; } +void NonClientFrameView::ActivationChanged(bool active) { +} + void NonClientFrameView::GetAccessibleState(ui::AXViewState* state) { state->role = ui::AX_ROLE_CLIENT; } @@ -320,7 +321,8 @@ const char* NonClientFrameView::GetClassName() const { //////////////////////////////////////////////////////////////////////////////// // NonClientFrameView, protected: -NonClientFrameView::NonClientFrameView() : inactive_rendering_disabled_(false) { +NonClientFrameView::NonClientFrameView() + : active_state_override_(nullptr) { SetEventTargeter( scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); } diff --git a/chromium/ui/views/window/non_client_view.h b/chromium/ui/views/window/non_client_view.h index 65ac395e63c..40000243b30 100644 --- a/chromium/ui/views/window/non_client_view.h +++ b/chromium/ui/views/window/non_client_view.h @@ -42,14 +42,9 @@ class VIEWS_EXPORT NonClientFrameView : public View, ~NonClientFrameView() override; - // Sets whether the window should be rendered as active regardless of the - // actual active state. Used when bubbles become active to make their parent - // appear active. A value of true makes the window render as active always, - // false gives normal behavior. - void SetInactiveRenderingDisabled(bool disable); - // Used to determine if the frame should be painted as active. Keyed off the - // window's actual active state and |inactive_rendering_disabled_|. + // window's actual active state and whether the widget should be rendered as + // active. bool ShouldPaintAsActive() const; // Helper for non-client view implementations to determine which area of the @@ -70,6 +65,12 @@ class VIEWS_EXPORT NonClientFrameView : public View, virtual gfx::Rect GetWindowBoundsForClientBounds( const gfx::Rect& client_bounds) const = 0; + // Gets the clip mask (in this View's parent's coordinates) that should be + // applied to the client view. Returns false if no special clip should be + // used. + virtual bool GetClientMask(const gfx::Size& size, + gfx::Path* mask) const; + // This function must ask the ClientView to do a hittest. We don't do this in // the parent NonClientView because that makes it more difficult to calculate // hittests for regions that are partially obscured by the ClientView, e.g. @@ -89,6 +90,9 @@ class VIEWS_EXPORT NonClientFrameView : public View, // Whether the widget can be resized or maximized has changed. virtual void SizeConstraintsChanged() = 0; + // The widget's activation state has changed to |active|. + virtual void ActivationChanged(bool active); + // View: void GetAccessibleState(ui::AXViewState* state) override; const char* GetClassName() const override; @@ -103,10 +107,17 @@ class VIEWS_EXPORT NonClientFrameView : public View, // View: void OnBoundsChanged(const gfx::Rect& previous_bounds) override; + void set_active_state_override(bool* active_state_override) { + active_state_override_ = active_state_override; + } + private: - // Prevents the non-client frame view from being rendered as inactive when - // true. - bool inactive_rendering_disabled_; + // Used to force ShouldPaintAsActive() to treat the active state a particular + // way. This is normally null; when non-null, its value will override the + // normal "active" value computed by the function. + bool* active_state_override_; + + DISALLOW_COPY_AND_ASSIGN(NonClientFrameView); }; //////////////////////////////////////////////////////////////////////////////// @@ -174,11 +185,6 @@ class VIEWS_EXPORT NonClientView : public View, public ViewTargeterDelegate { // or frame style. void UpdateFrame(); - // Prevents the window from being rendered as deactivated when |disable| is - // true, until called with |disable| false. Used when a sub-window is to be - // shown that shouldn't visually de-activate the window. - void SetInactiveRenderingDisabled(bool disable); - // Returns the bounds of the window required to display the content area at // the specified bounds. gfx::Rect GetWindowBoundsForClientBounds(const gfx::Rect client_bounds) const; diff --git a/chromium/ui/views_content_client/views_content_browser_client.cc b/chromium/ui/views_content_client/views_content_browser_client.cc index e5f4f38807e..29b6decd0be 100644 --- a/chromium/ui/views_content_client/views_content_browser_client.cc +++ b/chromium/ui/views_content_client/views_content_browser_client.cc @@ -4,9 +4,6 @@ #include "ui/views_content_client/views_content_browser_client.h" -#include <utility> - -#include "content/shell/browser/shell_browser_context.h" #include "ui/views_content_client/views_content_client_main_parts.h" namespace ui { @@ -27,15 +24,4 @@ content::BrowserMainParts* ViewsContentBrowserClient::CreateBrowserMainParts( return views_content_main_parts_; } -net::URLRequestContextGetter* -ViewsContentBrowserClient::CreateRequestContext( - content::BrowserContext* content_browser_context, - content::ProtocolHandlerMap* protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors) { - content::ShellBrowserContext* shell_context = - views_content_main_parts_->browser_context(); - return shell_context->CreateRequestContext(protocol_handlers, - std::move(request_interceptors)); -} - } // namespace ui diff --git a/chromium/ui/views_content_client/views_content_browser_client.h b/chromium/ui/views_content_client/views_content_browser_client.h index 98d9ee41b7c..408fb8afdbd 100644 --- a/chromium/ui/views_content_client/views_content_browser_client.h +++ b/chromium/ui/views_content_client/views_content_browser_client.h @@ -8,10 +8,6 @@ #include "base/macros.h" #include "content/public/browser/content_browser_client.h" -namespace content { -class ShellBrowserContext; -} - namespace ui { class ViewsContentClient; @@ -26,10 +22,6 @@ class ViewsContentBrowserClient : public content::ContentBrowserClient { // content::ContentBrowserClient: content::BrowserMainParts* CreateBrowserMainParts( const content::MainFunctionParams& parameters) override; - net::URLRequestContextGetter* CreateRequestContext( - content::BrowserContext* browser_context, - content::ProtocolHandlerMap* protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors) override; private: ViewsContentClientMainParts* views_content_main_parts_; diff --git a/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc b/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc index d13997147b5..36d24429a40 100644 --- a/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc +++ b/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc @@ -10,29 +10,12 @@ #include "ui/gfx/screen.h" #include "ui/views_content_client/views_content_client.h" #include "ui/views_content_client/views_content_client_main_parts_aura.h" -#include "ui/wm/core/nested_accelerator_controller.h" -#include "ui/wm/core/nested_accelerator_delegate.h" #include "ui/wm/test/wm_test_helper.h" namespace ui { namespace { -// A dummy version of the delegate usually provided by the Ash Shell. -class NestedAcceleratorDelegate : public ::wm::NestedAcceleratorDelegate { - public: - NestedAcceleratorDelegate() {} - ~NestedAcceleratorDelegate() override {} - - // ::wm::NestedAcceleratorDelegate: - Result ProcessAccelerator(const ui::Accelerator& accelerator) override { - return RESULT_NOT_PROCESSED; - } - - private: - DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorDelegate); -}; - class ViewsContentClientMainPartsChromeOS : public ViewsContentClientMainPartsAura { public: @@ -49,7 +32,6 @@ class ViewsContentClientMainPartsChromeOS // Enable a minimal set of views::corewm to be initialized. scoped_ptr<gfx::Screen> test_screen_; scoped_ptr< ::wm::WMTestHelper> wm_test_helper_; - scoped_ptr< ::wm::NestedAcceleratorController> nested_accelerator_controller_; DISALLOW_COPY_AND_ASSIGN(ViewsContentClientMainPartsChromeOS); }; @@ -65,7 +47,7 @@ void ViewsContentClientMainPartsChromeOS::PreMainMessageLoopRun() { gfx::Size host_size(800, 600); test_screen_.reset(aura::TestScreen::Create(host_size)); - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_.get()); + gfx::Screen::SetScreenInstance(test_screen_.get()); // Set up basic pieces of views::corewm. wm_test_helper_.reset( new ::wm::WMTestHelper(host_size, content::GetContextFactory())); @@ -75,16 +57,9 @@ void ViewsContentClientMainPartsChromeOS::PreMainMessageLoopRun() { // Ensure Aura knows where to open new windows. aura::Window* root_window = wm_test_helper_->host()->window(); views_content_client()->task().Run(browser_context(), root_window); - - nested_accelerator_controller_.reset( - new ::wm::NestedAcceleratorController(new NestedAcceleratorDelegate)); - aura::client::SetDispatcherClient(root_window, - nested_accelerator_controller_.get()); } void ViewsContentClientMainPartsChromeOS::PostMainMessageLoopRun() { - aura::client::SetDispatcherClient(wm_test_helper_->host()->window(), NULL); - nested_accelerator_controller_.reset(); wm_test_helper_.reset(); test_screen_.reset(); diff --git a/chromium/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc b/chromium/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc index 1ac04ecbb53..c7a3d0c7ee3 100644 --- a/chromium/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc +++ b/chromium/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc @@ -39,8 +39,7 @@ void ViewsContentClientMainPartsDesktopAura::PreMainMessageLoopRun() { ViewsContentClientMainPartsAura::PreMainMessageLoopRun(); aura::Env::CreateInstance(true); - gfx::Screen::SetScreenInstance( - gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen()); + gfx::Screen::SetScreenInstance(views::CreateDesktopScreen()); views_content_client()->task().Run(browser_context(), NULL); } diff --git a/chromium/ui/views_content_client/views_content_client_main_parts_mac.mm b/chromium/ui/views_content_client/views_content_client_main_parts_mac.mm index 3def22c7a94..bc0c85ef866 100644 --- a/chromium/ui/views_content_client/views_content_client_main_parts_mac.mm +++ b/chromium/ui/views_content_client/views_content_client_main_parts_mac.mm @@ -54,10 +54,6 @@ ViewsContentClientMainPartsMac::ViewsContentClientMainPartsMac( base::FilePath child_process_exe; PathService::Get(content::CHILD_PROCESS_EXE, &child_process_exe); - // Disable plugin discovery since NPAPI plugin support on Mac requires this to - // be done in a utility process type which isn't bundled with this executable. - content::PluginService::GetInstance()->DisablePluginsDiscoveryForTesting(); - app_controller_.reset([[ViewsContentClientAppController alloc] init]); [[NSApplication sharedApplication] setDelegate:app_controller_]; } diff --git a/chromium/ui/webui/resources/cr_elements/compiled_resources.gyp b/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp index 7b24a067bda..cbd32348c3b 100644 --- a/chromium/ui/webui/resources/cr_elements/compiled_resources.gyp +++ b/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp @@ -7,8 +7,8 @@ 'target_name': 'cr_elements_resources', 'type': 'none', 'dependencies': [ - 'network/compiled_resources.gyp:*', - 'policy/compiled_resources.gyp:*', + 'network/compiled_resources2.gyp:*', + 'policy/compiled_resources2.gyp:*', ], }, ] diff --git a/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.html b/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.html index fc17728bd78..f726fe3f366 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.html +++ b/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.html @@ -1,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <dom-module id="cr-events"> <script src="cr_events.js"></script> diff --git a/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.js b/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.js index 99d083791b5..dc82d6d71ba 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.js +++ b/chromium/ui/webui/resources/cr_elements/cr_events/cr_events.js @@ -13,8 +13,6 @@ * Usage: * * this.$.events.forward(this.$.element, ['change']); - * - * @element cr-events */ Polymer({ is: 'cr-events', diff --git a/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html b/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html index 26b367027d2..edcdd597f1a 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html +++ b/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html @@ -1,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> diff --git a/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js b/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js index c75d3622b73..a3a5cc20581 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js +++ b/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js @@ -10,9 +10,6 @@ * Example: * * <cr-expand-button expanded="{{sectionIsExpanded}}"></cr-expand-button> - * - * @group Chrome Elements - * @element cr-expand-button */ Polymer({ is: 'cr-expand-button', diff --git a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html index f8d203cc80e..8524c94860b 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html +++ b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html @@ -1,5 +1,5 @@ <link rel="import" href="chrome://resources/html/assert.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.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-icon-button/paper-icon-button.html"> diff --git a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.js b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.js index e24e3093538..a507fc34f66 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.js +++ b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.js @@ -29,21 +29,63 @@ var SearchField = Polymer({ showingSearch_: { type: Boolean, value: false, + observer: 'showingSearchChanged_', }, }, + /** + * Returns the value of the search field. + * @return {string} + */ + getValue: function() { + var searchInput = this.getSearchInput_(); + return searchInput ? searchInput.value : ''; + }, + + /** + * Sets the value of the search field, if it exists. + * @param {string} value + */ + setValue: function(value) { + var searchInput = this.getSearchInput_(); + if (searchInput) + searchInput.value = value; + }, + /** @param {SearchFieldDelegate} delegate */ setDelegate: function(delegate) { this.delegate_ = delegate; }, + /** @return {Promise<boolean>} */ + showAndFocus: function() { + this.showingSearch_ = true; + return this.focus_(); + }, + /** - * Returns the value of the search field. - * @return {string} + * @return {Promise<boolean>} + * @private */ - getValue: function() { - var searchInput = this.$$('#search-input'); - return searchInput ? searchInput.value : ''; + focus_: function() { + return new Promise(function(resolve) { + this.async(function() { + if (this.showingSearch_) { + var searchInput = this.getSearchInput_(); + if (searchInput) + searchInput.focus(); + } + resolve(this.showingSearch_); + }); + }.bind(this)); + }, + + /** + * @return {?Element} + * @private + */ + getSearchInput_: function() { + return this.$$('#search-input'); }, /** @private */ @@ -54,22 +96,27 @@ var SearchField = Polymer({ /** @private */ onSearchTermKeydown_: function(e) { - assert(this.showingSearch_); if (e.keyIdentifier == 'U+001B') // Escape. - this.toggleShowingSearch_(); + this.showingSearch_ = false; + }, + + /** @private */ + showingSearchChanged_: function() { + if (this.showingSearch_) { + this.focus_(); + return; + } + + var searchInput = this.getSearchInput_(); + if (!searchInput) + return; + + searchInput.value = ''; + this.onSearchTermSearch_(); }, /** @private */ toggleShowingSearch_: function() { this.showingSearch_ = !this.showingSearch_; - this.async(function() { - var searchInput = this.$$('#search-input'); - if (this.showingSearch_) { - searchInput.focus(); - } else { - searchInput.value = ''; - this.onSearchTermSearch_(); - } - }); }, }); diff --git a/chromium/ui/webui/resources/cr_elements/cr_shared_menu/compiled_resources.gyp b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/compiled_resources.gyp new file mode 100644 index 00000000000..ee4ebfb21af --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/compiled_resources.gyp @@ -0,0 +1,20 @@ +# 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': 'cr_shared_menu', + 'variables': { + 'depends': [ + '../../../../../third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js', + '../../js/assert.js', + '../../js/cr.js', + '../../js/cr/ui/position_util.js', + '../../js/util.js', + ], + }, + 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], + }, + ], +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_shared_menu/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/compiled_resources2.gyp new file mode 100644 index 00000000000..bcfaa7c1381 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/compiled_resources2.gyp @@ -0,0 +1,17 @@ +# 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': 'cr_shared_menu', + 'dependencies': [ + '../../js/compiled_resources2.gyp:assert', + '../../js/compiled_resources2.gyp:cr', + '../../js/compiled_resources2.gyp:util', + '../../js/cr/ui/compiled_resources2.gyp:position_util', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.html b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.html new file mode 100644 index 00000000000..6ff5f550d4d --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.html @@ -0,0 +1,27 @@ +<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.html"> +<link rel="import" href="chrome://resources/html/cr/ui/position_util.html"> +<link rel="import" href="chrome://resources/html/util.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/shadow.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-listbox/paper-listbox.html"> + +<dom-module id="cr-shared-menu"> + <template> + <style> + :host { + @apply(--shadow-elevation-2dp); + display: none; + position: absolute; + } + + :host([menu-open]) { + display: block; + } + </style> + <paper-listbox id="menu"> + <content></content> + </paper-listbox> + </template> + <script src="cr_shared_menu.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.js b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.js new file mode 100644 index 00000000000..9833a30a6b4 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.js @@ -0,0 +1,90 @@ +// 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. + +Polymer({ + is: 'cr-shared-menu', + + properties: { + menuOpen: { + type: Boolean, + value: false, + reflectToAttribute: true + }, + + /** + * The contextual item that this menu was clicked for. + * e.g. the data used to render an item in an <iron-list> or <dom-repeat> + * @type {?Object} + */ + itemData: { + type: Object, + value: null, + }, + }, + + /** + * The last anchor that was used to open a menu. It's necessary for toggling. + * @type {?Element} + */ + lastAnchor_: null, + + /** + * Adds listeners to the window in order to dismiss the menu on resize and + * when escape is pressed. + */ + attached: function() { + window.addEventListener('resize', this.closeMenu.bind(this)); + window.addEventListener('keydown', function(e) { + // Escape button on keyboard + if (e.keyCode == 27) + this.closeMenu(); + }.bind(this)); + }, + + /** Closes the menu. */ + closeMenu: function() { + this.menuOpen = false; + this.itemData = null; + }, + + /** + * Opens the menu at the anchor location. + * @param {!Element} anchor The location to display the menu. + * @param {!Object} itemData The contextual item's data. + */ + openMenu: function(anchor, itemData) { + this.menuOpen = true; + this.itemData = itemData; + this.lastAnchor_ = anchor; + + // Move the menu to the anchor. + var anchorRect = anchor.getBoundingClientRect(); + var parentRect = this.offsetParent.getBoundingClientRect(); + + var left = (isRTL() ? anchorRect.left : anchorRect.right) - parentRect.left; + var top = anchorRect.top - parentRect.top; + + cr.ui.positionPopupAtPoint(left, top, this, cr.ui.AnchorType.BEFORE); + + // Handle the bottom of the screen. + if (this.getBoundingClientRect().top != anchorRect.top) { + var bottom = anchorRect.bottom - parentRect.top; + cr.ui.positionPopupAtPoint(left, bottom, this, cr.ui.AnchorType.BEFORE); + } + + this.$.menu.focus(); + }, + + /** + * Toggles the menu for the anchor that is passed in. + * @param {!Element} anchor The location to display the menu. + * @param {!Object} itemData The contextual item's data. + */ + toggleMenu: function(anchor, itemData) { + if (anchor == this.lastAnchor_ && this.menuOpen) + this.closeMenu(); + else + this.openMenu(anchor, itemData); + }, +}); diff --git a/chromium/ui/webui/resources/cr_elements/demo_element.html b/chromium/ui/webui/resources/cr_elements/demo_element.html index 440d82ecffc..220741c82c9 100644 --- a/chromium/ui/webui/resources/cr_elements/demo_element.html +++ b/chromium/ui/webui/resources/cr_elements/demo_element.html @@ -1,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.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"> diff --git a/chromium/ui/webui/resources/cr_elements/network/compiled_resources.gyp b/chromium/ui/webui/resources/cr_elements/network/compiled_resources.gyp deleted file mode 100644 index 601aead59fd..00000000000 --- a/chromium/ui/webui/resources/cr_elements/network/compiled_resources.gyp +++ /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. -{ - 'targets': [ - { - 'target_name': 'cr_network_icon', - 'variables': { - 'depends': [ - 'cr_onc_types.js', - ], - 'externs': [ - '../../../../../third_party/closure_compiler/externs/networking_private.js' - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - { - 'target_name': 'cr_network_list', - 'variables': { - 'depends': [ - '../../../../../third_party/jstemplate/compiled_resources.gyp:jstemplate', - 'cr_onc_types.js', - 'cr_network_list_item.js', - '../../../../../ui/webui/resources/js/load_time_data.js', - ], - 'externs': [ - '../../../../../third_party/closure_compiler/externs/networking_private.js' - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - { - 'target_name': 'cr_network_select', - 'variables': { - 'depends': [ - 'compiled_resources.gyp:cr_network_list', - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - ], -} diff --git a/chromium/ui/webui/resources/cr_elements/network/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/network/compiled_resources2.gyp new file mode 100644 index 00000000000..2a193a33100 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/network/compiled_resources2.gyp @@ -0,0 +1,48 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'cr_network_icon', + 'dependencies': [ + 'cr_onc_types', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_network_icon_externs', + 'includes': ['../../../../../third_party/closure_compiler/include_js.gypi'], + }, + { + 'target_name': 'cr_network_list', + 'dependencies': [ + 'cr_onc_types', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_network_list_item', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + 'cr_onc_types', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_network_select', + 'dependencies': [ + '<(EXTERNS_GYP):networking_private', + 'cr_onc_types', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_onc_types', + 'dependencies': [ + '<(EXTERNS_GYP):networking_private', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.html b/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.html index ca8fd04730f..cb82c74b60e 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.html +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.html @@ -1,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/network/cr_onc_types.html"> <dom-module id="cr-network-icon"> diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.js b/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.js index d6ee5537f1a..13a3d6da9e0 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.js +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_icon.js @@ -46,7 +46,6 @@ function getIconTypeFromNetworkType(networkType) { /** * Polymer class definition for 'cr-network-icon'. - * @element cr-network-icon */ Polymer({ is: 'cr-network-icon', diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_list.html b/chromium/ui/webui/resources/cr_elements/network/cr_network_list.html index 7f2878604ed..2b8f82c58b2 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_list.html +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_list.html @@ -1,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> <link rel="import" href="chrome://resources/cr_elements/network/cr_network_list_item.html"> diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_list.js b/chromium/ui/webui/resources/cr_elements/network/cr_network_list.js index 04962be36ad..24178c4e6c4 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_list.js +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_list.js @@ -11,7 +11,6 @@ /** * Polymer class definition for 'cr-network-list'. * TODO(stevenjb): Update with iron-list(?) once implemented in Polymer 1.0. - * @element cr-network-list */ Polymer({ is: 'cr-network-list', diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.html b/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.html index 6be1c16ecb2..3fe72aafd28 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.html +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.html @@ -1,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.js b/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.js index 84d2c1375e6..b362f8ae202 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.js +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_list_item.js @@ -66,7 +66,6 @@ function getNetworkName(network) { /** * Polymer class definition for 'cr-network-list-item'. - * @element cr-network-list-item */ Polymer({ is: 'cr-network-list-item', diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_select.html b/chromium/ui/webui/resources/cr_elements/network/cr_network_select.html index bc703bcb914..b085806efb1 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_select.html +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_select.html @@ -1,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> <link rel="import" href="chrome://resources/cr_elements/network/cr_network_list.html"> diff --git a/chromium/ui/webui/resources/cr_elements/network/cr_network_select.js b/chromium/ui/webui/resources/cr_elements/network/cr_network_select.js index 6bc77b40ae7..ecf439385f5 100644 --- a/chromium/ui/webui/resources/cr_elements/network/cr_network_select.js +++ b/chromium/ui/webui/resources/cr_elements/network/cr_network_select.js @@ -7,9 +7,6 @@ * networkingPrivate calls to populate it. */ -/** - * @element cr-network-select - */ Polymer({ is: 'cr-network-select', diff --git a/chromium/ui/webui/resources/cr_elements/policy/compiled_resources.gyp b/chromium/ui/webui/resources/cr_elements/policy/compiled_resources.gyp deleted file mode 100644 index 4d3578649ab..00000000000 --- a/chromium/ui/webui/resources/cr_elements/policy/compiled_resources.gyp +++ /dev/null @@ -1,81 +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. -{ - 'targets': [ - { - 'target_name': 'cr_policy_indicator_behavior', - 'variables': { - 'depends': [ - '../../../../../ui/webui/resources/js/compiled_resources.gyp:assert', - '../../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - { - 'target_name': 'cr_policy_pref_behavior', - 'variables': { - 'depends': [ - '../../../../../third_party/polymer/v1_0/components-chromium/iron-iconset-svg/iron-iconset-svg-extracted.js', - '../../../../../third_party/polymer/v1_0/components-chromium/iron-meta/iron-meta-extracted.js', - '../../../../../ui/webui/resources/js/compiled_resources.gyp:assert', - '../../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', - 'cr_policy_indicator_behavior.js', - ], - 'externs': [ - '../../../../../third_party/closure_compiler/externs/settings_private.js' - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - { - 'target_name': 'cr_policy_pref_indicator', - 'variables': { - 'depends': [ - '../../../../../ui/webui/resources/js/compiled_resources.gyp:assert', - '../../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', - 'cr_policy_indicator_behavior.js', - 'cr_policy_pref_behavior.js', - ], - 'externs': [ - '../../../../../third_party/closure_compiler/externs/settings_private.js' - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - { - 'target_name': 'cr_policy_network_behavior', - 'variables': { - 'depends': [ - '../../../../../third_party/polymer/v1_0/components-chromium/iron-iconset-svg/iron-iconset-svg-extracted.js', - '../../../../../third_party/polymer/v1_0/components-chromium/iron-meta/iron-meta-extracted.js', - '../../../../../ui/webui/resources/js/compiled_resources.gyp:assert', - '../../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', - '../network/cr_onc_types.js', - 'cr_policy_indicator_behavior.js', - ], - 'externs': [ - '../../../../../third_party/closure_compiler/externs/networking_private.js', - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - { - 'target_name': 'cr_policy_network_indicator', - 'variables': { - 'depends': [ - '../../../../../ui/webui/resources/js/compiled_resources.gyp:assert', - '../../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', - '../network/cr_onc_types.js', - 'cr_policy_indicator_behavior.js', - 'cr_policy_network_behavior.js', - ], - 'externs': [ - '../../../../../third_party/closure_compiler/externs/networking_private.js', - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], - }, - ], -} diff --git a/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp new file mode 100644 index 00000000000..7e71e2bdb5e --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp @@ -0,0 +1,44 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'cr_policy_indicator_behavior', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_policy_pref_behavior', + 'dependencies': [ + 'cr_policy_indicator_behavior', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_policy_pref_indicator', + 'dependencies': [ + '<(EXTERNS_GYP):settings_private', + 'cr_policy_indicator_behavior', + 'cr_policy_pref_behavior', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_policy_network_behavior', + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'cr_policy_network_indicator', + 'dependencies': [ + '../network/compiled_resources2.gyp:cr_onc_types', + 'cr_policy_indicator_behavior', + 'cr_policy_network_behavior', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} 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 377786542ed..3c86d9937c5 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,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.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/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/social-icons.html"> diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.js b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.js index d036c06973b..29c09719c33 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.js +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.js @@ -7,7 +7,6 @@ * properties. */ -/** @element cr-policy-network-indicator */ Polymer({ is: 'cr-policy-network-indicator', 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 49edb142edc..9fa274a89cc 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,4 +1,4 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.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/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/social-icons.html"> diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js index 769ba62f791..cd2562b1713 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js @@ -5,8 +5,6 @@ /** * @fileoverview Polymer element for indicating policies that apply to an * element controlling a settings preference. - * - * @element cr-policy-pref-indicator */ Polymer({ is: 'cr-policy-pref-indicator', diff --git a/chromium/ui/webui/resources/cr_elements_resources.grdp b/chromium/ui/webui/resources/cr_elements_resources.grdp index 66b50842c66..368f5a997df 100644 --- a/chromium/ui/webui/resources/cr_elements_resources.grdp +++ b/chromium/ui/webui/resources/cr_elements_resources.grdp @@ -117,6 +117,12 @@ <structure name="IDR_CR_ELEMENTS_CR_SEARCH_FIELD_JS" file="../../webui/resources/cr_elements/cr_search_field/cr_search_field.js" type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_CR_SHARED_MENU_HTML" + file="../../webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.html" + type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_CR_SHARED_MENU_JS" + file="../../webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.js" + type="chrome_html" /> <structure name="IDR_CR_ELEMENTS_SHARED_CSS" file="../../webui/resources/cr_elements/shared.css" type="chrome_html" /> diff --git a/chromium/ui/webui/resources/css/bubble.css b/chromium/ui/webui/resources/css/bubble.css index b0092e5e45c..52e0d102a53 100644 --- a/chromium/ui/webui/resources/css/bubble.css +++ b/chromium/ui/webui/resources/css/bubble.css @@ -68,7 +68,7 @@ html[dir='rtl'] .bubble-close { } .bubble-arrow { - -webkit-transform: rotate(45deg); + transform: rotate(45deg); box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.15); height: 15px; position: absolute; diff --git a/chromium/ui/webui/resources/css/dialogs.css b/chromium/ui/webui/resources/css/dialogs.css index 9c16aca557b..fde32aab91e 100644 --- a/chromium/ui/webui/resources/css/dialogs.css +++ b/chromium/ui/webui/resources/css/dialogs.css @@ -38,16 +38,16 @@ @-webkit-keyframes pulse { 0% { - -webkit-transform: scale(1); + transform: scale(1); } 40% { - -webkit-transform: scale(1.02); + transform: scale(1.02); } 60% { - -webkit-transform: scale(1.02); + transform: scale(1.02); } 100% { - -webkit-transform: scale(1); + transform: scale(1); } } @@ -59,13 +59,13 @@ } .shown > .cr-dialog-frame { - -webkit-transform: perspective(500px) scale(1) + transform: perspective(500px) scale(1) translateY(0) rotateX(0); opacity: 1; } .cr-dialog-frame { - -webkit-transform: perspective(500px) scale(0.99) + transform: perspective(500px) scale(0.99) translateY(-20px) rotateX(5deg); -webkit-transition: all 180ms; -webkit-transition-duration: 250ms; diff --git a/chromium/ui/webui/resources/css/overlay.css b/chromium/ui/webui/resources/css/overlay.css index 361fb8218ad..3b3340d26de 100644 --- a/chromium/ui/webui/resources/css/overlay.css +++ b/chromium/ui/webui/resources/css/overlay.css @@ -25,14 +25,14 @@ .overlay.transparent .page { /* TODO(flackr): Add perspective(500px) rotateX(5deg) when accelerated * compositing is enabled on chrome:// pages. See http://crbug.com/116800. */ - -webkit-transform: scale(0.99) translateY(-20px); + transform: scale(0.99) translateY(-20px); } /* The foreground dialog. */ .overlay .page { -webkit-border-radius: 3px; -webkit-box-orient: vertical; - -webkit-transition: 200ms -webkit-transform; + -webkit-transition: 200ms transform; background: white; box-shadow: 0 4px 23px 5px rgba(0, 0, 0, 0.2), 0 2px 6px rgba(0,0,0,0.15); color: #333; @@ -52,16 +52,16 @@ /* keyframes used to pulse the overlay */ @-webkit-keyframes pulse { 0% { - -webkit-transform: scale(1); + transform: scale(1); } 40% { - -webkit-transform: scale(1.02); + transform: scale(1.02); } 60% { - -webkit-transform: scale(1.02); + transform: scale(1.02); } 100% { - -webkit-transform: scale(1); + transform: scale(1); } } diff --git a/chromium/ui/webui/resources/css/text_defaults.css b/chromium/ui/webui/resources/css/text_defaults.css index 037ade7efb1..ac989687c0f 100644 --- a/chromium/ui/webui/resources/css/text_defaults.css +++ b/chromium/ui/webui/resources/css/text_defaults.css @@ -13,17 +13,17 @@ * * 2. via the webui::AppendWebUICSSTextDefaults() method to directly append it * to an HTML string. - * Otherwise its $placeholders won't be expanded. */ + * Otherwise its placeholders won't be expanded. */ html { - direction: ${textDirection}; + direction: $i18n{textDirection}; } body { - font-family: ${fontFamily}; - font-size: ${fontSize}; + font-family: $i18nRaw{fontFamily}; + font-size: $i18n{fontSize}; } button { - font-family: ${fontFamily}; + font-family: $i18nRaw{fontFamily}; } diff --git a/chromium/ui/webui/resources/css/text_defaults_md.css b/chromium/ui/webui/resources/css/text_defaults_md.css index a2b2022667a..b378ba293f0 100644 --- a/chromium/ui/webui/resources/css/text_defaults_md.css +++ b/chromium/ui/webui/resources/css/text_defaults_md.css @@ -13,19 +13,19 @@ * * 2. via the webui::AppendWebUICSSTextDefaultsMd() method to directly append it * to an HTML string. - * Otherwise its $placeholders won't be expanded. */ + * Otherwise its placeholders won't be expanded. */ @import url(chrome://resources/css/roboto.css); html { - direction: ${textDirection}; + direction: $i18n{textDirection}; } body { - font-family: Roboto, ${fontFamily}; - font-size: ${fontSize}; + font-family: Roboto, $i18nRaw{fontFamily}; + font-size: 81.25% } button { - font-family: Roboto, ${fontFamily}; + font-family: Roboto, $i18nRaw{fontFamily}; } diff --git a/chromium/ui/webui/resources/css/trash.css b/chromium/ui/webui/resources/css/trash.css index fb06e9aa5b7..dfe66dc0c3f 100644 --- a/chromium/ui/webui/resources/css/trash.css +++ b/chromium/ui/webui/resources/css/trash.css @@ -29,7 +29,7 @@ .trash > .lid { -webkit-transform-origin: -7% 100%; - -webkit-transition: -webkit-transform 150ms; + -webkit-transition: transform 150ms; height: 6px; width: 14px; } @@ -39,12 +39,12 @@ html[dir='rtl'] .trash > .lid { } .trash:-webkit-any(:focus, :hover, .open) > .lid { - -webkit-transform: rotate(-45deg); - -webkit-transition: -webkit-transform 250ms; + transform: rotate(-45deg); + -webkit-transition: transform 250ms; } html[dir='rtl'] .trash:-webkit-any(:focus, :hover, .open) > .lid { - -webkit-transform: rotate(45deg); + transform: rotate(45deg); } .trash > .can { diff --git a/chromium/ui/webui/resources/css/tree.css b/chromium/ui/webui/resources/css/tree.css index a47735a1f3f..a86baba5b08 100644 --- a/chromium/ui/webui/resources/css/tree.css +++ b/chromium/ui/webui/resources/css/tree.css @@ -22,7 +22,7 @@ tree { } .expand-icon { - -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); -webkit-transition: all 150ms; background: url(../images/tree_triangle.svg) no-repeat center center; background-size: 8px 5px; @@ -36,11 +36,11 @@ tree { } html[dir=rtl] .expand-icon { - -webkit-transform: rotate(90deg); + transform: rotate(90deg); } .tree-item[expanded] > .tree-row > .expand-icon { - -webkit-transform: rotate(0); + transform: rotate(0); background-image: url(../images/tree_triangle.svg); opacity: .5; } diff --git a/chromium/ui/webui/resources/css/widgets.css b/chromium/ui/webui/resources/css/widgets.css index 4897138a2a4..5a5dbc21c69 100644 --- a/chromium/ui/webui/resources/css/widgets.css +++ b/chromium/ui/webui/resources/css/widgets.css @@ -145,6 +145,7 @@ input[type='radio']:checked::before { top: 3px; } +<if expr="not is_ios"> /* Hover **********************************************************************/ :enabled:hover:-webkit-any( @@ -167,6 +168,7 @@ input[type='radio']:checked::before { background-image: url(../images/select.png), -webkit-linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0); } +</if> /* Active *********************************************************************/ diff --git a/chromium/ui/webui/resources/html/cr.html b/chromium/ui/webui/resources/html/cr.html index 6e7b3b83471..df43b18b41a 100644 --- a/chromium/ui/webui/resources/html/cr.html +++ b/chromium/ui/webui/resources/html/cr.html @@ -1 +1,2 @@ +<link rel="import" href="chrome://resources/html/promise_resolver.html"> <script src="chrome://resources/js/cr.js"></script> diff --git a/chromium/ui/webui/resources/html/polymer.html b/chromium/ui/webui/resources/html/polymer.html new file mode 100644 index 00000000000..4b9e0414092 --- /dev/null +++ b/chromium/ui/webui/resources/html/polymer.html @@ -0,0 +1,2 @@ +<script src="chrome://resources/js/polymer_config.js"></script> +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> diff --git a/chromium/ui/webui/resources/html/polymer_config.html b/chromium/ui/webui/resources/html/polymer_config.html deleted file mode 100644 index d805abd7ba7..00000000000 --- a/chromium/ui/webui/resources/html/polymer_config.html +++ /dev/null @@ -1 +0,0 @@ -<script src="chrome://resources/js/polymer_config.js"></script> diff --git a/chromium/ui/webui/resources/html/promise_resolver.html b/chromium/ui/webui/resources/html/promise_resolver.html new file mode 100644 index 00000000000..3a171fa9e64 --- /dev/null +++ b/chromium/ui/webui/resources/html/promise_resolver.html @@ -0,0 +1 @@ +<script src="chrome://resources/js/promise_resolver.js"></script> diff --git a/chromium/ui/webui/resources/html/web_ui_listener_behavior.html b/chromium/ui/webui/resources/html/web_ui_listener_behavior.html new file mode 100644 index 00000000000..1c1906de592 --- /dev/null +++ b/chromium/ui/webui/resources/html/web_ui_listener_behavior.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="chrome://resources/js/web_ui_listener_behavior.js"></script> diff --git a/chromium/ui/webui/resources/images/2x/otr_icon_standalone.png b/chromium/ui/webui/resources/images/2x/otr_icon_standalone.png Binary files differdeleted file mode 100644 index af06732e608..00000000000 --- a/chromium/ui/webui/resources/images/2x/otr_icon_standalone.png +++ /dev/null diff --git a/chromium/ui/webui/resources/images/icon_bookmarks.svg b/chromium/ui/webui/resources/images/icon_bookmarks.svg new file mode 100644 index 00000000000..cc93d8cbc65 --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_bookmarks.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> + <path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/> + <path d="M0 0h24v24H0z" fill="none"/> +</svg> diff --git a/chromium/ui/webui/resources/images/icon_extensions.svg b/chromium/ui/webui/resources/images/icon_extensions.svg new file mode 100644 index 00000000000..e678d5839bb --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_extensions.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> + <path d="M0 0h24v24H0z" fill="none"/> + <path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/> +</svg> diff --git a/chromium/ui/webui/resources/images/icon_history.svg b/chromium/ui/webui/resources/images/icon_history.svg new file mode 100644 index 00000000000..8c0d7762881 --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_history.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> + <path d="M0 0h24v24H0z" fill="none"/> + <path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/> +</svg> diff --git a/chromium/ui/webui/resources/images/icon_passwords.svg b/chromium/ui/webui/resources/images/icon_passwords.svg new file mode 100644 index 00000000000..246a9dbeee8 --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_passwords.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="40" height="30" viewBox="0 0 40 30"> + <path d="M3 5.005v19.99C3 26.103 3.895 27 5 27h30c1.108 0 2-.898 2-2.005V5.005C37 3.897 36.105 3 35 3H5c-1.108 0-2 .898-2 2.005zm-3 0C0 2.242 2.234 0 5 0h30c2.763 0 5 2.242 5 5.005v19.99C40 27.758 37.766 30 35 30H5c-2.763 0-5-2.242-5-5.005V5.005zM29 8h3v13h-3V8zm-6 6.14l-.632-1.834-.147.046-3.195.98V10H16.98v3.33l-3.2-.978-.148-.046L13 14.14l.146.045 3.21.984-1.987 2.58-.09.115L15.93 19l.09-.118L18 16.316l1.978 2.566.09.118 1.655-1.133-.09-.117-1.988-2.58 3.21-.985.145-.045zM7.222 12.352l-3.2.98V10H3v6.316l1.977 2.566.09.118 1.654-1.133-.09-.117-1.985-2.58 3.21-.985L8 14.14l-.63-1.834-.148.046z" fill-rule="evenodd"/> +</svg> diff --git a/chromium/ui/webui/resources/images/icon_tabs.svg b/chromium/ui/webui/resources/images/icon_tabs.svg new file mode 100644 index 00000000000..9fbe81c7f88 --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_tabs.svg @@ -0,0 +1,6 @@ +<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> + <g fill="none" fill-rule="evenodd"> + <path d="M0 0h24v24H0V0z"/> + <path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13zM6 4h5v16H6V4zm5 7h7v9h-7v-9z" fill="#000"/> + </g> +</svg> diff --git a/chromium/ui/webui/resources/images/icon_themes.svg b/chromium/ui/webui/resources/images/icon_themes.svg new file mode 100644 index 00000000000..6e225fdfe91 --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_themes.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="32" height="30" viewBox="0 0 32 30"> + <path d="M20.04 16.74L12.776 24l-2.42-2.42c-.668-.668 0-2.42 0-2.42l1.21-3.632c.302-.91.364-2.057 0-2.422-3.63-3.63-8.472-6.05-9.684-7.26-1.337-1.338-1.337-3.507 0-4.842 1.34-1.34 3.506-1.34 4.842 0 1.21 1.21 3.63 6.05 7.263 9.682.363.363 1.512.303 2.42 0l3.63-1.21s1.754-.668 2.422 0l2.42 2.42-4.84 4.843zM3.657 2.44c-.586.585-.586 1.535 0 2.12.585.586 1.535.586 2.122 0 .582-.585.582-1.535 0-2.12-.59-.587-1.54-.587-2.125 0zM24.32 24.9h-3.4v3.4l-1.7 1.7-5.1-5.1L26.02 13l5.1 5.1-6.8 6.8z" fill-rule="evenodd"/> +</svg> diff --git a/chromium/ui/webui/resources/images/otr_icon_standalone.png b/chromium/ui/webui/resources/images/otr_icon_standalone.png Binary files differdeleted file mode 100644 index 27d3dc44045..00000000000 --- a/chromium/ui/webui/resources/images/otr_icon_standalone.png +++ /dev/null diff --git a/chromium/ui/webui/resources/images/throbber_medium.svg b/chromium/ui/webui/resources/images/throbber_medium.svg index 2a7aaca7af9..c6f51b277eb 100644 --- a/chromium/ui/webui/resources/images/throbber_medium.svg +++ b/chromium/ui/webui/resources/images/throbber_medium.svg @@ -42,8 +42,8 @@ /* Rotating the whole thing */ @-webkit-keyframes rotate { - from {-webkit-transform: rotate(0deg);} - to {-webkit-transform: rotate(360deg);} + from {transform: rotate(0deg);} + to {transform: rotate(360deg);} } .qp-circular-loader { -webkit-animation-name: rotate; @@ -68,10 +68,10 @@ } @-webkit-keyframes rot { from { - -webkit-transform: rotate(0deg); + transform: rotate(0deg); } to { - -webkit-transform: rotate(-360deg); + transform: rotate(-360deg); } } @-webkit-keyframes colors { diff --git a/chromium/ui/webui/resources/images/throbber_small.svg b/chromium/ui/webui/resources/images/throbber_small.svg index 0cf4db122e9..6f31d493fd8 100644 --- a/chromium/ui/webui/resources/images/throbber_small.svg +++ b/chromium/ui/webui/resources/images/throbber_small.svg @@ -43,8 +43,8 @@ /* Rotating the whole thing */ @-webkit-keyframes rotate { - from {-webkit-transform: rotate(0deg);} - to {-webkit-transform: rotate(360deg);} + from {transform: rotate(0deg);} + to {transform: rotate(360deg);} } .qp-circular-loader { -webkit-animation-name: rotate; @@ -69,10 +69,10 @@ } @-webkit-keyframes rot { from { - -webkit-transform: rotate(0deg); + transform: rotate(0deg); } to { - -webkit-transform: rotate(-360deg); + transform: rotate(-360deg); } } @-webkit-keyframes colors { diff --git a/chromium/ui/webui/resources/js/action_link.js b/chromium/ui/webui/resources/js/action_link.js index bd607f524b1..70b89e728a6 100644 --- a/chromium/ui/webui/resources/js/action_link.js +++ b/chromium/ui/webui/resources/js/action_link.js @@ -44,7 +44,7 @@ var ActionLink = document.registerElement('action-link', { this.setAttribute('role', 'link'); this.addEventListener('keydown', function(e) { - if (!this.disabled && e.keyIdentifier == 'Enter') { + if (!this.disabled && e.keyIdentifier == 'Enter' && !this.href) { // Schedule a click asynchronously because other 'keydown' handlers // may still run later (e.g. document.addEventListener('keydown')). // Specifically options dialogs break when this timeout isn't here. diff --git a/chromium/ui/webui/resources/js/assert.js b/chromium/ui/webui/resources/js/assert.js index 9b0be57b318..94f25358294 100644 --- a/chromium/ui/webui/resources/js/assert.js +++ b/chromium/ui/webui/resources/js/assert.js @@ -62,7 +62,11 @@ function assertNotReached(opt_message) { * @template T */ function assertInstanceof(value, type, opt_message) { - assert(value instanceof type, - opt_message || value + ' is not a[n] ' + (type.name || typeof type)); + // We don't use assert immediately here so that we avoid constructing an error + // message if we don't have to. + if (!(value instanceof type)) { + assertNotReached(opt_message || 'Value ' + value + + ' is not a[n] ' + (type.name || typeof type)); + } return value; } diff --git a/chromium/ui/webui/resources/js/chromeos/compiled_resources.gyp b/chromium/ui/webui/resources/js/chromeos/compiled_resources2.gyp index 05e7e0a9be9..a363475a954 100644 --- a/chromium/ui/webui/resources/js/chromeos/compiled_resources.gyp +++ b/chromium/ui/webui/resources/js/chromeos/compiled_resources2.gyp @@ -5,13 +5,11 @@ 'targets': [ { 'target_name': 'ui_account_tweaks', - 'variables': { - 'depends': [ - '../compiled_resources.gyp:cr', - '../compiled_resources.gyp:load_time_data', - ], - }, - 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'], + '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/js/compiled_resources.gyp b/chromium/ui/webui/resources/js/compiled_resources.gyp index cbfeab6278a..d679f4c32a2 100644 --- a/chromium/ui/webui/resources/js/compiled_resources.gyp +++ b/chromium/ui/webui/resources/js/compiled_resources.gyp @@ -10,6 +10,7 @@ { 'target_name': 'cr', 'variables': { + 'depends': ['compiled_resources.gyp:promise_resolver'], 'externs': ['../../../../third_party/closure_compiler/externs/chrome_send.js'], }, 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], @@ -19,6 +20,22 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], }, { + 'target_name': 'i18n_template_no_process', + 'variables': { + 'depends': ['compiled_resources.gyp:load_time_data'], + 'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'], + }, + 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], + }, + { + 'target_name': 'i18n_template', + 'variables': { + 'depends': ['compiled_resources.gyp:load_time_data'], + 'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'], + }, + 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], + }, + { 'target_name': 'load_time_data', 'variables': { 'depends': [ @@ -30,31 +47,19 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], }, { - 'target_name': 'util', - 'variables': { - 'depends': ['compiled_resources.gyp:cr'], - # TODO(jlklein): Get <(VARIABLES) in transient externs/depends working. - 'externs': ['../../../../third_party/closure_compiler/externs/chrome_send.js'], - }, - 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], - }, - { 'target_name': 'parse_html_subset', 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], }, { - 'target_name': 'i18n_template_no_process', - 'variables': { - 'depends': ['compiled_resources.gyp:load_time_data'], - 'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'], - }, + 'target_name': 'promise_resolver', 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], }, { - 'target_name': 'i18n_template', + 'target_name': 'util', 'variables': { - 'depends': ['compiled_resources.gyp:load_time_data'], - 'externs': ['../../../../third_party/closure_compiler/externs/pending_compiler_externs.js'], + 'depends': ['compiled_resources.gyp:cr', 'assert.js'], + # TODO(jlklein): Get <(VARIABLES) in transient externs/depends working. + 'externs': ['../../../../third_party/closure_compiler/externs/chrome_send.js'], }, 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], }, diff --git a/chromium/ui/webui/resources/js/compiled_resources2.gyp b/chromium/ui/webui/resources/js/compiled_resources2.gyp index 2d0ce6627ae..e24ce72d519 100644 --- a/chromium/ui/webui/resources/js/compiled_resources2.gyp +++ b/chromium/ui/webui/resources/js/compiled_resources2.gyp @@ -14,47 +14,66 @@ { 'target_name': 'cr', 'dependencies': [ + 'promise_resolver', '<(EXTERNS_GYP):chrome_send', 'assert', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'load_time_data', - 'dependencies': ['<(DEPTH)/third_party/jstemplate/compiled_resources2.gyp:jstemplate'], + 'target_name': 'event_tracker', 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'event_tracker', + 'target_name': 'i18n_template_no_process', + 'dependencies': [ + 'load_time_data', + '<(EXTERNS_GYP):pending_compiler_externs', + ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'util', + 'target_name': 'i18n_template', 'dependencies': [ - '<(EXTERNS_GYP):chrome_send', - 'cr', + 'load_time_data', + # Ideally, <include> would automatically import externs as well, but + # it current doesn't and that sounds hard. Let's just kill <include>. + '<(EXTERNS_GYP):pending_compiler_externs', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'i18n_behavior', + 'dependencies': [ + 'load_time_data', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'load_time_data', + 'dependencies': ['<(DEPTH)/third_party/jstemplate/compiled_resources2.gyp:jstemplate'], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'parse_html_subset', 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'i18n_template_no_process', + 'target_name': 'promise_resolver', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'util', 'dependencies': [ - 'load_time_data', - '<(EXTERNS_GYP):pending_compiler_externs', + '<(EXTERNS_GYP):chrome_send', + 'cr', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'i18n_template', + 'target_name': 'web_ui_listener_behavior', 'dependencies': [ - 'load_time_data', - # Ideally, <include> would automatically import externs as well, but - # it current doesn't and that sounds hard. Let's just kill <include>. - '<(EXTERNS_GYP):pending_compiler_externs', + 'cr', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/ui/webui/resources/js/cr.js b/chromium/ui/webui/resources/js/cr.js index a38c59f8473..090cfd94f7d 100644 --- a/chromium/ui/webui/resources/js/cr.js +++ b/chromium/ui/webui/resources/js/cr.js @@ -9,8 +9,11 @@ */ var global = this; +/** @typedef {{eventName: string, uid: number}} */ +var WebUIListener; + /** Platform, package, object property, and Event support. **/ -var cr = function() { +var cr = cr || function() { 'use strict'; /** @@ -22,6 +25,8 @@ var cr = function() { * @param {*=} opt_object The object to expose at the end of the path. * @param {Object=} opt_objectToExportTo The object to add the path to; * default is {@code global}. + * @return {!Object} The last object exported (i.e. exportPath('cr.ui') + * returns a reference to the ui property of window.cr). * @private */ function exportPath(name, opt_object, opt_objectToExportTo) { @@ -39,7 +44,7 @@ var cr = function() { } } return cur; - }; + } /** * Fires a property change event on the target. @@ -313,56 +318,62 @@ var cr = function() { } /** - * The mapping used by the sendWithCallback mechanism to tie the callback - * supplied to an invocation of sendWithCallback with the WebUI response - * sent by the browser in response to the chrome.send call. The mapping is - * from ID to callback function; the ID is generated by sendWithCallback and - * is unique across all invocations of said method. - * @type {!Object<Function>} + * The mapping used by the sendWithPromise mechanism to tie the Promise + * returned to callers with the corresponding WebUI response. The mapping is + * from ID to the PromiseResolver helper; the ID is generated by + * sendWithPromise and is unique across all invocations of said method. + * @type {!Object<!PromiseResolver>} */ - var chromeSendCallbackMap = Object.create(null); + var chromeSendResolverMap = {}; /** * The named method the WebUI handler calls directly in response to a - * chrome.send call that expects a callback. The handler requires no knowledge + * chrome.send call that expects a response. The handler requires no knowledge * of the specific name of this method, as the name is passed to the handler * as the first argument in the arguments list of chrome.send. The handler * must pass the ID, also sent via the chrome.send arguments list, as the * first argument of the JS invocation; additionally, the handler may - * supply any number of other arguments that will be forwarded to the - * callback. - * @param {string} id The unique ID identifying the callback method this - * response is tied to. + * supply any number of other arguments that will be included in the response. + * @param {string} id The unique ID identifying the Promise this response is + * tied to. + * @param {boolean} isSuccess Whether the request was successful. + * @param {*} response The response as sent from C++. */ - function webUIResponse(id) { - chromeSendCallbackMap[id].apply( - null, Array.prototype.slice.call(arguments, 1)); - delete chromeSendCallbackMap[id]; + function webUIResponse(id, isSuccess, response) { + var resolver = chromeSendResolverMap[id]; + delete chromeSendResolverMap[id]; + + if (isSuccess) + resolver.resolve(response); + else + resolver.reject(response); } /** - * A variation of chrome.send which allows the client to receive a direct - * callback without requiring the handler to have specific knowledge of any - * JS internal method names or state. The callback will be removed from the - * mapping once it has fired. + * A variation of chrome.send, suitable for messages that expect a single + * response from C++. * @param {string} methodName The name of the WebUI handler API. - * @param {Array|undefined} args Arguments for the method call sent to the - * WebUI handler. Pass undefined if no args should be sent to the handler. - * @param {Function} callback A callback function which is called (indirectly) - * by the WebUI handler. + * @param {...*} var_args Varibale number of arguments to be forwarded to the + * C++ call. + * @return {!Promise} */ - function sendWithCallback(methodName, args, callback) { - var id = methodName + createUid(); - chromeSendCallbackMap[id] = callback; - chrome.send(methodName, ['cr.webUIResponse', id].concat(args || [])); + function sendWithPromise(methodName, var_args) { + var args = Array.prototype.slice.call(arguments, 1); + var promiseResolver = new PromiseResolver(); + var id = methodName + '_' + createUid(); + chromeSendResolverMap[id] = promiseResolver; + chrome.send(methodName, [id].concat(args)); + return promiseResolver.promise; } /** - * A registry of callbacks keyed by event name. Used by addWebUIListener to - * register listeners. - * @type {!Object<Array<Function>>} + * A map of maps associating event names with listeners. The 2nd level map + * associates a listener ID with the callback function, such that individual + * listeners can be removed from an event without affecting other listeners of + * the same event. + * @type {!Object<!Object<!Function>>} */ - var webUIListenerMap = Object.create(null); + var webUIListenerMap = {}; /** * The named method the WebUI handler calls directly when an event occurs. @@ -370,26 +381,52 @@ var cr = function() { * of the JS invocation; additionally, the handler may supply any number of * other arguments that will be forwarded to the listener callbacks. * @param {string} event The name of the event that has occurred. + * @param {...*} var_args Additional arguments passed from C++. */ - function webUIListenerCallback(event) { - var listenerCallbacks = webUIListenerMap[event]; - for (var i = 0; i < listenerCallbacks.length; i++) { - var callback = listenerCallbacks[i]; - callback.apply(null, Array.prototype.slice.call(arguments, 1)); + function webUIListenerCallback(event, var_args) { + var eventListenersMap = webUIListenerMap[event]; + if (!eventListenersMap) { + // C++ event sent for an event that has no listeners. + // TODO(dpapad): Should a warning be displayed here? + return; + } + + var args = Array.prototype.slice.call(arguments, 1); + for (var listenerId in eventListenersMap) { + eventListenersMap[listenerId].apply(null, args); } } /** * Registers a listener for an event fired from WebUI handlers. Any number of * listeners may register for a single event. - * @param {string} event The event to listen to. - * @param {Function} callback The callback run when the event is fired. + * @param {string} eventName The event to listen to. + * @param {!Function} callback The callback run when the event is fired. + * @return {!WebUIListener} An object to be used for removing a listener via + * cr.removeWebUIListener. Should be treated as read-only. */ - function addWebUIListener(event, callback) { - if (event in webUIListenerMap) - webUIListenerMap[event].push(callback); - else - webUIListenerMap[event] = [callback]; + function addWebUIListener(eventName, callback) { + webUIListenerMap[eventName] = webUIListenerMap[eventName] || {}; + var uid = createUid(); + webUIListenerMap[eventName][uid] = callback; + return {eventName: eventName, uid: uid}; + } + + /** + * Removes a listener. Does nothing if the specified listener is not found. + * @param {!WebUIListener} listener The listener to be removed (as returned by + * addWebUIListener). + * @return {boolean} Whether the given listener was found and actually + * removed. + */ + function removeWebUIListener(listener) { + var listenerExists = webUIListenerMap[listener.eventName] && + webUIListenerMap[listener.eventName][listener.uid]; + if (listenerExists) { + delete webUIListenerMap[listener.eventName][listener.uid]; + return true; + } + return false; } return { @@ -402,12 +439,15 @@ var cr = function() { exportPath: exportPath, getUid: getUid, makePublic: makePublic, - webUIResponse: webUIResponse, - sendWithCallback: sendWithCallback, - webUIListenerCallback: webUIListenerCallback, - addWebUIListener: addWebUIListener, PropertyKind: PropertyKind, + // C++ <-> JS communication related methods. + addWebUIListener: addWebUIListener, + removeWebUIListener: removeWebUIListener, + sendWithPromise: sendWithPromise, + webUIListenerCallback: webUIListenerCallback, + webUIResponse: webUIResponse, + get doc() { return document; }, @@ -431,5 +471,10 @@ var cr = function() { get isLinux() { return /Linux/.test(navigator.userAgent); }, + + /** Whether this is on Android. */ + get isAndroid() { + return /Android/.test(navigator.userAgent); + } }; }(); diff --git a/chromium/ui/webui/resources/js/cr/ui/card_slider.js b/chromium/ui/webui/resources/js/cr/ui/card_slider.js index b8684228962..f19199125a1 100644 --- a/chromium/ui/webui/resources/js/cr/ui/card_slider.js +++ b/chromium/ui/webui/resources/js/cr/ui/card_slider.js @@ -328,7 +328,7 @@ cr.define('cr.ui', function() { }, /** - * Handles the ends of -webkit-transitions on -webkit-transform (animated + * Handles the ends of -webkit-transitions on transform (animated * card switches). * @param {Event} e The webkitTransitionEnd event. * @private @@ -381,11 +381,10 @@ cr.define('cr.ui', function() { /** * Append a card to the end of the list. - * @param {!Node} card A card to add at the end of the card slider. + * @param {!Element} card A card to add at the end of the card slider. */ appendCard: function(card) { - assert(card instanceof Node, '|card| isn\'t a Node'); - this.cards_.push(card); + this.cards_.push(assertInstanceof(card, Element)); this.fireAddedEvent_(card, this.cards_.length - 1); }, @@ -419,11 +418,11 @@ cr.define('cr.ui', function() { * Removes a card by index from the card slider. If the card to be removed * is the current card or in front of the current card, the current card * will be updated (to current card - 1). - * @param {!Node} card A card to be removed. + * @param {!Element} card A card to be removed. */ removeCard: function(card) { - assert(card instanceof Node, '|card| isn\'t a Node'); - this.removeCardAtIndex(this.cards_.indexOf(card)); + this.removeCardAtIndex( + this.cards_.indexOf(assertInstanceof(card, Element))); }, /** @@ -460,7 +459,7 @@ cr.define('cr.ui', function() { }, /** - * This re-syncs the -webkit-transform that's used to position the frame in + * This re-syncs the transform that's used to position the frame in * the likely event it needs to be updated by a card being inserted or * removed in the flow. */ @@ -534,7 +533,7 @@ cr.define('cr.ui', function() { /** * Selects a card from the stack. Passes through to selectCard. - * @param {Node} newCard The card that should be selected. + * @param {!Element} newCard The card that should be selected. * @param {boolean=} opt_animate Whether to animate. */ selectCardByValue: function(newCard, opt_animate) { @@ -567,7 +566,7 @@ cr.define('cr.ui', function() { // enough to change cards. var transition = ''; if (opt_animate) { - transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ + + transition = 'transform ' + CardSlider.TRANSITION_TIME_ + 'ms ease-in-out'; } this.container_.style.WebkitTransition = transition; @@ -582,12 +581,12 @@ cr.define('cr.ui', function() { * @private */ translateTo_: function(x) { - // We use a webkitTransform to slide because this is GPU accelerated on + // We use a transform to slide because this is GPU accelerated on // Chrome and iOS. Once Chrome does GPU acceleration on the position // fixed-layout elements we could simply set the element's position to // fixed and modify 'left' instead. this.deltaX_ = x - this.currentLeft_; - this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)'; + this.container_.style.transform = 'translate3d(' + x + 'px, 0, 0)'; }, /* Touch ******************************************************************/ diff --git a/chromium/ui/webui/resources/js/cr/ui/compiled_resources.gyp b/chromium/ui/webui/resources/js/cr/ui/compiled_resources.gyp index 1d4cde6a5bb..8f06cd10f02 100644 --- a/chromium/ui/webui/resources/js/cr/ui/compiled_resources.gyp +++ b/chromium/ui/webui/resources/js/cr/ui/compiled_resources.gyp @@ -18,6 +18,7 @@ 'variables': { 'depends': [ '../../cr.js', + '../../promise_resolver.js', '../../util.js', ], 'externs': [ diff --git a/chromium/ui/webui/resources/js/cr/ui/compiled_resources2.gyp b/chromium/ui/webui/resources/js/cr/ui/compiled_resources2.gyp index a420949cea1..1bc7c7fcb1e 100644 --- a/chromium/ui/webui/resources/js/cr/ui/compiled_resources2.gyp +++ b/chromium/ui/webui/resources/js/cr/ui/compiled_resources2.gyp @@ -4,10 +4,18 @@ { 'targets': [ { + 'target_name': 'alert_overlay', + 'dependencies': [ + '../../compiled_resources2.gyp:cr', + '../../compiled_resources2.gyp:util', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'command', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/cr/compiled_resources2.gyp:ui', + '../../compiled_resources2.gyp:cr', + '../compiled_resources2.gyp:ui', ], 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -27,6 +35,11 @@ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'focus_outline_manager', + 'dependencies': ['../../compiled_resources2.gyp:cr'], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'focus_row', 'dependencies': [ '../../compiled_resources2.gyp:assert', @@ -37,6 +50,39 @@ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'menu_button', + 'dependencies': [ + '../../compiled_resources2.gyp:assert', + '../../compiled_resources2.gyp:cr', + '../../compiled_resources2.gyp:event_tracker', + '../compiled_resources2.gyp:ui', + 'menu', + 'menu_item', + 'position_util', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'menu_item', + 'dependencies': [ + '../../compiled_resources2.gyp:cr', + '../../compiled_resources2.gyp:load_time_data', + '../compiled_resources2.gyp:ui', + 'command', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'menu', + 'dependencies': [ + '../../compiled_resources2.gyp:assert', + '../../compiled_resources2.gyp:cr', + '../compiled_resources2.gyp:ui', + 'menu_item', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'overlay', 'dependencies': [ '../../compiled_resources2.gyp:cr', @@ -44,5 +90,12 @@ ], 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], }, + { + 'target_name': 'position_util', + 'dependencies': [ + '../../compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, ], } diff --git a/chromium/ui/webui/resources/js/cr/ui/focus_grid.js b/chromium/ui/webui/resources/js/cr/ui/focus_grid.js index 5e9590883f9..4eda10b6af0 100644 --- a/chromium/ui/webui/resources/js/cr/ui/focus_grid.js +++ b/chromium/ui/webui/resources/js/cr/ui/focus_grid.js @@ -82,7 +82,7 @@ cr.define('cr.ui', function() { }, /** - * @param {Node} target A target item to find in this grid. + * @param {!Element} target A target item to find in this grid. * @return {number} The row index. -1 if not found. */ getRowIndexForTarget: function(target) { @@ -122,7 +122,7 @@ cr.define('cr.ui', function() { addRowBefore: function(row, nextRow) { row.delegate = row.delegate || this; - var nextRowIndex = this.rows.indexOf(nextRow); + var nextRowIndex = nextRow ? this.rows.indexOf(nextRow) : -1; if (nextRowIndex == -1) this.rows.push(row); else @@ -134,7 +134,7 @@ cr.define('cr.ui', function() { * @param {cr.ui.FocusRow} row The row that needs to be removed. */ removeRow: function(row) { - var nextRowIndex = this.rows.indexOf(row); + var nextRowIndex = row ? this.rows.indexOf(row) : -1; if (nextRowIndex > -1) this.rows.splice(nextRowIndex, 1); }, diff --git a/chromium/ui/webui/resources/js/cr/ui/focus_row.js b/chromium/ui/webui/resources/js/cr/ui/focus_row.js index bb5471e9761..a545d14d7be 100644 --- a/chromium/ui/webui/resources/js/cr/ui/focus_row.js +++ b/chromium/ui/webui/resources/js/cr/ui/focus_row.js @@ -17,7 +17,8 @@ cr.define('cr.ui', function() { * * @param {!Element} root The root of this focus row. Focus classes are * applied to |root| and all added elements must live within |root|. - * @param {?Node} boundary Focus events are ignored outside of this node. + * @param {?Element} boundary Focus events are ignored outside of this + * element. * @param {cr.ui.FocusRow.Delegate=} opt_delegate An optional event delegate. * @constructor */ @@ -25,8 +26,8 @@ cr.define('cr.ui', function() { /** @type {!Element} */ this.root = root; - /** @private {!Node} */ - this.boundary_ = boundary || document; + /** @private {!Element} */ + this.boundary_ = boundary || document.documentElement; /** @type {cr.ui.FocusRow.Delegate|undefined} */ this.delegate = opt_delegate; @@ -221,10 +222,11 @@ cr.define('cr.ui', function() { * @private */ onBlur_: function(e) { - if (!this.boundary_.contains(/** @type {Node} */(e.relatedTarget))) + if (!this.boundary_.contains(/** @type {Element} */(e.relatedTarget))) return; - if (this.getFocusableElements().indexOf(e.currentTarget) >= 0) + var currentTarget = /** @type {!Element} */(e.currentTarget); + if (this.getFocusableElements().indexOf(currentTarget) >= 0) this.makeActive(false); }, @@ -257,7 +259,8 @@ cr.define('cr.ui', function() { */ onKeydown_: function(e) { var elements = this.getFocusableElements(); - var elementIndex = elements.indexOf(e.currentTarget); + var currentElement = /** @type {!Element} */(e.currentTarget); + var elementIndex = elements.indexOf(currentElement); assert(elementIndex >= 0); if (this.delegate && this.delegate.onKeydown(this, e)) diff --git a/chromium/ui/webui/resources/js/cr/ui/list_selection_model.js b/chromium/ui/webui/resources/js/cr/ui/list_selection_model.js index 550f4b52614..f9aca63c668 100644 --- a/chromium/ui/webui/resources/js/cr/ui/list_selection_model.js +++ b/chromium/ui/webui/resources/js/cr/ui/list_selection_model.js @@ -65,6 +65,7 @@ cr.define('cr.ui', function() { } for (var index in unselected) { + index = +index; delete this.selectedIndexes_[index]; // Mark the index as changed. If previously marked, then unmark, // since it just got reverted to the original state. diff --git a/chromium/ui/webui/resources/js/cr/ui/menu_item.js b/chromium/ui/webui/resources/js/cr/ui/menu_item.js index 2b8b12339b7..b4726f282f9 100644 --- a/chromium/ui/webui/resources/js/cr/ui/menu_item.js +++ b/chromium/ui/webui/resources/js/cr/ui/menu_item.js @@ -161,10 +161,12 @@ cr.define('cr.ui', function() { var shortcutText = ''; // TODO(zvorygin): if more cornercases appear - optimize following - // code. Currently 'Enter' keystroke is passed as 'Enter', and 'Space' - // is passed as 'U+0020' + // code. Currently 'Enter' keystroke is passed as 'Enter', but 'Space' + // and 'Backspace' are passed as 'U+0020' and 'U+0008'. if (ident == 'U+0020') ident = 'Space'; + else if (ident == 'U+0008') + ident = 'Backspace'; ['CTRL', 'ALT', 'SHIFT', 'META'].forEach(function(mod) { if (mods[mod]) @@ -197,7 +199,8 @@ cr.define('cr.ui', function() { if (!this.disabled && !this.isSeparator() && this.selected) { // Store |contextElement| since it'll be removed by {Menu} on handling // 'activate' event. - var contextElement = this.parentNode.contextElement; + var contextElement = /** @type {{contextElement: Element}} */( + this.parentNode).contextElement; var activationEvent = cr.doc.createEvent('Event'); activationEvent.initEvent('activate', true, true); activationEvent.originalEvent = e; diff --git a/chromium/ui/webui/resources/js/cr/ui/overlay.js b/chromium/ui/webui/resources/js/cr/ui/overlay.js index 56f339f1b1d..7d02f13999b 100644 --- a/chromium/ui/webui/resources/js/cr/ui/overlay.js +++ b/chromium/ui/webui/resources/js/cr/ui/overlay.js @@ -13,7 +13,8 @@ cr.define('cr.ui.overlay', function() { * @return {HTMLElement} The overlay. */ function getTopOverlay() { - var overlays = document.querySelectorAll('.overlay:not([hidden])'); + var overlays = /** @type !NodeList<!HTMLElement> */( + document.querySelectorAll('.overlay:not([hidden])')); return overlays[overlays.length - 1]; } @@ -26,8 +27,8 @@ cr.define('cr.ui.overlay', function() { */ function getDefaultButton(overlay) { function isHidden(node) { return node.hidden; } - var defaultButtons = - overlay.querySelectorAll('.page .button-strip > .default-button'); + var defaultButtons = /** @type !NodeList<!HTMLElement> */( + overlay.querySelectorAll('.page .button-strip > .default-button')); for (var i = 0; i < defaultButtons.length; i++) { if (!findAncestor(defaultButtons[i], isHidden)) return defaultButtons[i]; diff --git a/chromium/ui/webui/resources/js/cr/ui/page_manager/page_manager.js b/chromium/ui/webui/resources/js/cr/ui/page_manager/page_manager.js index 8fafc82c4f3..9d2492b3d02 100644 --- a/chromium/ui/webui/resources/js/cr/ui/page_manager/page_manager.js +++ b/chromium/ui/webui/resources/js/cr/ui/page_manager/page_manager.js @@ -61,7 +61,8 @@ cr.define('cr.ui.pageManager', function() { this.handleScroll_(); // Shake the dialog if the user clicks outside the dialog bounds. - var containers = document.querySelectorAll('body > .overlay'); + var containers = /** @type {!NodeList<!HTMLElement>} */( + document.querySelectorAll('body > .overlay')); for (var i = 0; i < containers.length; i++) { var overlay = containers[i]; cr.ui.overlay.setupOverlay(overlay); diff --git a/chromium/ui/webui/resources/js/cr/ui/position_util.js b/chromium/ui/webui/resources/js/cr/ui/position_util.js index 14b29d92d97..c0c483b9196 100644 --- a/chromium/ui/webui/resources/js/cr/ui/position_util.js +++ b/chromium/ui/webui/resources/js/cr/ui/position_util.js @@ -226,8 +226,9 @@ cr.define('cr.ui', function() { * @param {number} x The client x position. * @param {number} y The client y position. * @param {!HTMLElement} popupElement The popup element we are positioning. + * @param {cr.ui.AnchorType=} opt_anchorType The type of anchoring we want. */ - function positionPopupAtPoint(x, y, popupElement) { + function positionPopupAtPoint(x, y, popupElement, opt_anchorType) { var rect = { left: x, top: y, @@ -236,7 +237,9 @@ cr.define('cr.ui', function() { right: x, bottom: y }; - positionPopupAroundRect(rect, popupElement, AnchorType.BELOW); + + var anchorType = opt_anchorType || AnchorType.BELOW; + positionPopupAroundRect(rect, popupElement, anchorType); } // Export diff --git a/chromium/ui/webui/resources/js/cr/ui/splitter.js b/chromium/ui/webui/resources/js/cr/ui/splitter.js index 2c1799e4f22..c457e421e9e 100644 --- a/chromium/ui/webui/resources/js/cr/ui/splitter.js +++ b/chromium/ui/webui/resources/js/cr/ui/splitter.js @@ -71,6 +71,15 @@ cr.define('cr.ui', function() { true); this.addEventListener('touchstart', this.handleTouchStart_.bind(this), true); + this.resizeNextElement_ = false; + }, + + /** + * @param {boolean} resizeNext True if resize the next element. + * By default, splitter resizes previous (left) element. + */ + set resizeNextElement(resizeNext) { + this.resizeNextElement_ = resizeNext; }, /** @@ -129,12 +138,33 @@ cr.define('cr.ui', function() { }, /** + * @return {Element} + * @private + */ + getResizeTarget_: function() { + return this.resizeNextElement_ ? this.nextElementSibling : + this.previousElementSibling; + }, + + /** + * Calculate width to resize target element. + * @param {number} deltaX horizontal drag amount + * @return {number} + * @private + */ + calcDeltaX_: function(deltaX) { + return this.resizeNextElement_ ? -deltaX : deltaX; + }, + + /** * Handles the mousedown event which starts the dragging of the splitter. * @param {!Event} e The mouse event. * @private */ handleMouseDown_: function(e) { e = /** @type {!MouseEvent} */(e); + if (e.button) + return; this.startDrag(e.clientX, false); // Default action is to start selection and to move focus. e.preventDefault(); @@ -203,10 +233,10 @@ cr.define('cr.ui', function() { handleSplitterDragStart: function() { // Use the computed width style as the base so that we can ignore what // box sizing the element has. - var leftComponent = this.previousElementSibling; - var doc = leftComponent.ownerDocument; + var targetElement = this.getResizeTarget_(); + var doc = targetElement.ownerDocument; this.startWidth_ = parseFloat( - doc.defaultView.getComputedStyle(leftComponent).width); + doc.defaultView.getComputedStyle(targetElement).width); }, /** @@ -215,8 +245,9 @@ cr.define('cr.ui', function() { * @protected */ handleSplitterDragMove: function(deltaX) { - var leftComponent = this.previousElementSibling; - leftComponent.style.width = this.startWidth_ + deltaX + 'px'; + var targetElement = this.getResizeTarget_(); + var newWidth = this.startWidth_ + this.calcDeltaX_(deltaX); + targetElement.style.width = newWidth + 'px'; }, /** @@ -226,10 +257,10 @@ cr.define('cr.ui', function() { */ handleSplitterDragEnd: function() { // Check if the size changed. - var leftComponent = this.previousElementSibling; - var doc = leftComponent.ownerDocument; + var targetElement = this.getResizeTarget_(); + var doc = targetElement.ownerDocument; var computedWidth = parseFloat( - doc.defaultView.getComputedStyle(leftComponent).width); + doc.defaultView.getComputedStyle(targetElement).width); if (this.startWidth_ != computedWidth) cr.dispatchSimpleEvent(this, 'resize'); }, diff --git a/chromium/ui/webui/resources/js/event_tracker.js b/chromium/ui/webui/resources/js/event_tracker.js index f2ec3e207c7..126c6f5b247 100644 --- a/chromium/ui/webui/resources/js/event_tracker.js +++ b/chromium/ui/webui/resources/js/event_tracker.js @@ -17,7 +17,7 @@ * is fixed. * @typedef {{target: !EventTarget, * eventType: string, - * listener: Function, + * listener: (EventListener|Function), * capture: boolean}} */ var EventTrackerEntry; diff --git a/chromium/ui/webui/resources/js/i18n_behavior.js b/chromium/ui/webui/resources/js/i18n_behavior.js index 98cf154ac32..64dd57c4253 100644 --- a/chromium/ui/webui/resources/js/i18n_behavior.js +++ b/chromium/ui/webui/resources/js/i18n_behavior.js @@ -11,18 +11,17 @@ * behaviors: [ * I18nBehavior, * ], - * - * @group Chrome UI Behavior */ /** @polymerBehavior */ var I18nBehavior = { /** * @param {string} id The ID of the string to translate. - * @param {...string} var_args Placeholders required by the string ($1-9). + * @param {...string} var_args Placeholders required by the string ($0-9). * @return {string} A translated, substituted string. */ i18n: function(id, var_args) { - return loadTimeData.getStringF.apply(loadTimeData, arguments); + return arguments.length == 1 ? loadTimeData.getString(id) : + loadTimeData.getStringF.apply(loadTimeData, arguments); }, }; diff --git a/chromium/ui/webui/resources/js/promise_resolver.js b/chromium/ui/webui/resources/js/promise_resolver.js new file mode 100644 index 00000000000..0759c955584 --- /dev/null +++ b/chromium/ui/webui/resources/js/promise_resolver.js @@ -0,0 +1,35 @@ +// 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 PromiseResolver is a helper class that allows creating a + * Promise that will be fulfilled (resolved or rejected) some time later. + * + * Example: + * var resolver = new PromiseResolver(); + * resolver.promise.then(function(result) { + * console.log('resolved with', result); + * }); + * ... + * ... + * resolver.resolve({hello: 'world'}); + */ + +/** + * @constructor @struct + * @template T + */ +function PromiseResolver() { + /** @type {function(T): void} */ + this.resolve; + + /** @type {function(*=): void} */ + this.reject; + + /** @type {!Promise<T>} */ + this.promise = new Promise(function(resolve, reject) { + this.resolve = resolve; + this.reject = reject; + }.bind(this)); +} diff --git a/chromium/ui/webui/resources/js/util.js b/chromium/ui/webui/resources/js/util.js index 3907c5215aa..82a4e50328d 100644 --- a/chromium/ui/webui/resources/js/util.js +++ b/chromium/ui/webui/resources/js/util.js @@ -5,12 +5,25 @@ // <include src="assert.js"> /** - * Alias for document.getElementById. + * Alias for document.getElementById. Found elements must be HTMLElements. * @param {string} id The ID of the element to find. * @return {HTMLElement} The found element or null if not found. */ function $(id) { - return document.getElementById(id); + var el = document.getElementById(id); + return el ? assertInstanceof(el, HTMLElement) : null; +} + +// TODO(devlin): This should return SVGElement, but closure compiler is missing +// those externs. +/** + * Alias for document.getElementById. Found elements must be SVGElements. + * @param {string} id The ID of the element to find. + * @return {Element} The found element or null if not found. + */ +function getSVGElement(id) { + var el = document.getElementById(id); + return el ? assertInstanceof(el, Element) : null; } /** @@ -33,25 +46,6 @@ function announceAccessibleMessage(msg) { } /** - * Calls chrome.send with a callback and restores the original afterwards. - * @param {string} name The name of the message to send. - * @param {!Array} params The parameters to send. - * @param {string} callbackName The name of the function that the backend calls. - * @param {!Function} callback The function to call. - */ -function chromeSend(name, params, callbackName, callback) { - var old = global[callbackName]; - global[callbackName] = function() { - // restore - global[callbackName] = old; - - var args = Array.prototype.slice.call(arguments); - return callback.apply(global, args); - }; - chrome.send(name, params); -} - -/** * Returns the scale factors supported by this platform for webui * resources. * @return {Array} The supported scale factors. @@ -380,10 +374,19 @@ function createElementWithClassName(type, className) { * or when no paint happens during the animation). This function sets up * a timer and emulate the event if it is not fired when the timer expires. * @param {!HTMLElement} el The element to watch for webkitTransitionEnd. - * @param {number} timeOut The maximum wait time in milliseconds for the - * webkitTransitionEnd to happen. + * @param {number=} opt_timeOut The maximum wait time in milliseconds for the + * webkitTransitionEnd to happen. If not specified, it is fetched from |el| + * using the transitionDuration style value. */ -function ensureTransitionEndEvent(el, timeOut) { +function ensureTransitionEndEvent(el, opt_timeOut) { + if (opt_timeOut === undefined) { + var style = getComputedStyle(el); + opt_timeOut = parseFloat(style.transitionDuration) * 1000; + + // Give an additional 50ms buffer for the animation to complete. + opt_timeOut += 50; + } + var fired = false; el.addEventListener('webkitTransitionEnd', function f(e) { el.removeEventListener('webkitTransitionEnd', f); @@ -392,7 +395,7 @@ function ensureTransitionEndEvent(el, timeOut) { window.setTimeout(function() { if (!fired) cr.dispatchSimpleEvent(el, 'webkitTransitionEnd', true); - }, timeOut); + }, opt_timeOut); } /** @@ -462,3 +465,12 @@ function elide(original, maxLength) { return original; return original.substring(0, maxLength - 1) + '\u2026'; } + +/** + * Quote a string so it can be used in a regular expression. + * @param {string} str The source string. + * @return {string} The escaped string. + */ +function quoteString(str) { + return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); +} diff --git a/chromium/ui/webui/resources/js/web_ui_listener_behavior.js b/chromium/ui/webui/resources/js/web_ui_listener_behavior.js new file mode 100644 index 00000000000..d7fc56b5fff --- /dev/null +++ b/chromium/ui/webui/resources/js/web_ui_listener_behavior.js @@ -0,0 +1,43 @@ +// 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 Behavior to be used by Polymer elements that want to + * automatically remove WebUI listeners when detached. + */ + +/** @polymerBehavior */ +var WebUIListenerBehavior = { + properties: { + /** + * Holds WebUI listeners that need to be removed when this element is + * destroyed. + * @private {!Array<!WebUIListener>} + */ + webUIListeners_: { + type: Array, + value: function() { return []; }, + }, + }, + + /** + * Adds a WebUI listener and registers it for automatic removal when this + * element is detached. + * Note: Do not use this method if you intend to remove this listener + * manually (use cr.addWebUIListener directly instead). + * + * @param {string} eventName The event to listen to. + * @param {!Function} callback The callback run when the event is fired. + */ + addWebUIListener: function(eventName, callback) { + this.webUIListeners_.push(cr.addWebUIListener(eventName, callback)); + }, + + /** @override */ + detached: function() { + while (this.webUIListeners_.length > 0) { + cr.removeWebUIListener(this.webUIListeners_.pop()); + } + }, +}; diff --git a/chromium/ui/webui/resources/polymer_resources.grdp b/chromium/ui/webui/resources/polymer_resources.grdp index 59fd3245d66..562f6b0c1ca 100644 --- a/chromium/ui/webui/resources/polymer_resources.grdp +++ b/chromium/ui/webui/resources/polymer_resources.grdp @@ -80,9 +80,15 @@ <structure name="IDR_POLYMER_1_0_IRON_FLEX_LAYOUT_CLASSES_IRON_FLEX_LAYOUT_HTML" file="../../../third_party/polymer/v1_0/components-chromium/iron-flex-layout/classes/iron-flex-layout.html" type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_IRON_FLEX_LAYOUT_CLASSES_IRON_FLEX_LAYOUT_EXTRACTED_JS" + file="../../../third_party/polymer/v1_0/components-chromium/iron-flex-layout/classes/iron-flex-layout-extracted.js" + type="chrome_html" /> <structure name="IDR_POLYMER_1_0_IRON_FLEX_LAYOUT_CLASSES_IRON_SHADOW_FLEX_LAYOUT_HTML" file="../../../third_party/polymer/v1_0/components-chromium/iron-flex-layout/classes/iron-shadow-flex-layout.html" type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_IRON_FLEX_LAYOUT_CLASSES_IRON_SHADOW_FLEX_LAYOUT_EXTRACTED_JS" + file="../../../third_party/polymer/v1_0/components-chromium/iron-flex-layout/classes/iron-shadow-flex-layout-extracted.js" + type="chrome_html" /> <structure name="IDR_POLYMER_1_0_IRON_FLEX_LAYOUT_IRON_FLEX_LAYOUT_HTML" file="../../../third_party/polymer/v1_0/components-chromium/iron-flex-layout/iron-flex-layout.html" type="chrome_html" /> @@ -131,6 +137,12 @@ <structure name="IDR_POLYMER_1_0_IRON_ICONSET_SVG_IRON_ICONSET_SVG_HTML" file="../../../third_party/polymer/v1_0/components-chromium/iron-iconset-svg/iron-iconset-svg.html" type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_IRON_IMAGE_IRON_IMAGE_EXTRACTED_JS" + file="../../../third_party/polymer/v1_0/components-chromium/iron-image/iron-image-extracted.js" + type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_IRON_IMAGE_IRON_IMAGE_HTML" + file="../../../third_party/polymer/v1_0/components-chromium/iron-image/iron-image.html" + type="chrome_html" /> <structure name="IDR_POLYMER_1_0_IRON_INPUT_IRON_INPUT_EXTRACTED_JS" file="../../../third_party/polymer/v1_0/components-chromium/iron-input/iron-input-extracted.js" type="chrome_html" /> @@ -203,6 +215,12 @@ <structure name="IDR_POLYMER_1_0_IRON_RESIZABLE_BEHAVIOR_IRON_RESIZABLE_BEHAVIOR_HTML" file="../../../third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.html" type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_IRON_SCROLL_TARGET_BEHAVIOR_IRON_SCROLL_TARGET_BEHAVIOR_EXTRACTED_JS" + file="../../../third_party/polymer/v1_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior-extracted.js" + type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_IRON_SCROLL_TARGET_BEHAVIOR_IRON_SCROLL_TARGET_BEHAVIOR_HTML" + file="../../../third_party/polymer/v1_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.html" + type="chrome_html" /> <structure name="IDR_POLYMER_1_0_IRON_SELECTOR_IRON_MULTI_SELECTABLE_EXTRACTED_JS" file="../../../third_party/polymer/v1_0/components-chromium/iron-selector/iron-multi-selectable-extracted.js" type="chrome_html" /> @@ -227,9 +245,6 @@ <structure name="IDR_POLYMER_1_0_IRON_SELECTOR_IRON_SELECTOR_HTML" file="../../../third_party/polymer/v1_0/components-chromium/iron-selector/iron-selector.html" type="chrome_html" /> - <structure name="IDR_POLYMER_1_0_IRON_TEST_HELPERS_IRON_TEST_HELPERS_EXTRACTED_JS" - file="../../../third_party/polymer/v1_0/components-chromium/iron-test-helpers/iron-test-helpers-extracted.js" - type="chrome_html" /> <structure name="IDR_POLYMER_1_0_IRON_TEST_HELPERS_IRON_TEST_HELPERS_HTML" file="../../../third_party/polymer/v1_0/components-chromium/iron-test-helpers/iron-test-helpers.html" type="chrome_html" /> @@ -281,6 +296,12 @@ <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_DOWN_ANIMATION_HTML" file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-down-animation.html" type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_FROM_BOTTOM_ANIMATION_EXTRACTED_JS" + file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-from-bottom-animation-extracted.js" + type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_FROM_BOTTOM_ANIMATION_HTML" + file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-from-bottom-animation.html" + type="chrome_html" /> <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_FROM_LEFT_ANIMATION_EXTRACTED_JS" file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-from-left-animation-extracted.js" type="chrome_html" /> @@ -293,6 +314,12 @@ <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_FROM_RIGHT_ANIMATION_HTML" file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-from-right-animation.html" type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_FROM_TOP_ANIMATION_EXTRACTED_JS" + file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-from-top-animation-extracted.js" + type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_FROM_TOP_ANIMATION_HTML" + file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-from-top-animation.html" + type="chrome_html" /> <structure name="IDR_POLYMER_1_0_NEON_ANIMATION_ANIMATIONS_SLIDE_LEFT_ANIMATION_EXTRACTED_JS" file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/animations/slide-left-animation-extracted.js" type="chrome_html" /> @@ -515,6 +542,12 @@ <structure name="IDR_POLYMER_1_0_PAPER_ITEM_PAPER_ITEM_HTML" file="../../../third_party/polymer/v1_0/components-chromium/paper-item/paper-item.html" type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_PAPER_LISTBOX_PAPER_LISTBOX_EXTRACTED_JS" + file="../../../third_party/polymer/v1_0/components-chromium/paper-listbox/paper-listbox-extracted.js" + type="chrome_html" /> + <structure name="IDR_POLYMER_1_0_PAPER_LISTBOX_PAPER_LISTBOX_HTML" + file="../../../third_party/polymer/v1_0/components-chromium/paper-listbox/paper-listbox.html" + type="chrome_html" /> <structure name="IDR_POLYMER_1_0_PAPER_MATERIAL_PAPER_MATERIAL_EXTRACTED_JS" file="../../../third_party/polymer/v1_0/components-chromium/paper-material/paper-material-extracted.js" type="chrome_html" /> diff --git a/chromium/ui/webui/resources/webui_resources.grd b/chromium/ui/webui/resources/webui_resources.grd index df687b739aa..aadc894027f 100644 --- a/chromium/ui/webui/resources/webui_resources.grd +++ b/chromium/ui/webui/resources/webui_resources.grd @@ -260,6 +260,8 @@ without changes to the corresponding grd file. --> file="html/action_link.html" type="chrome_html" /> <structure name="IDR_WEBUI_HTML_ASSERT" file="html/assert.html" type="chrome_html" /> + <structure name="IDR_WEBUI_HTML_PROMISE_RESOLVER" + file="html/promise_resolver.html" type="chrome_html" /> <structure name="IDR_WEBUI_HTML_CR" file="html/cr.html" type="chrome_html" /> <structure name="IDR_WEBUI_HTML_CR_EVENT_TARGET" @@ -301,17 +303,21 @@ without changes to the corresponding grd file. --> file="html/i18n_template.html" type="chrome_html" /> <structure name="IDR_WEBUI_HTML_LOAD_TIME_DATA" file="html/load_time_data.html" type="chrome_html" /> - <structure name="IDR_WEBUI_HTML_POLYMER_CONFIG" - file="html/polymer_config.html" type="chrome_html" /> + <structure name="IDR_WEBUI_HTML_POLYMER" + file="html/polymer.html" type="chrome_html" /> <structure name="IDR_WEBUI_HTML_I18N_BEHAVIOR" file="html/i18n_behavior.html" type="chrome_html" /> <structure name="IDR_WEBUI_HTML_UTIL" file="html/util.html" type="chrome_html" /> + <structure name="IDR_WEBUI_HTML_WEBUI_LISTENER_BEHAVIOR" + file="html/web_ui_listener_behavior.html" type="chrome_html" /> <structure name="IDR_WEBUI_JS_ACTION_LINK" file="js/action_link.js" type="chrome_html" /> <structure name="IDR_WEBUI_JS_ASSERT" file="js/assert.js" type="chrome_html" /> + <structure name="IDR_WEBUI_JS_PROMISE_RESOLVER" + file="js/promise_resolver.js" type="chrome_html" /> <structure name="IDR_WEBUI_JS_CR" file="js/cr.js" type="chrome_html" /> <structure name="IDR_WEBUI_JS_CR_EVENT_TARGET" @@ -434,6 +440,8 @@ without changes to the corresponding grd file. --> file="js/i18n_behavior.js" type="chrome_html" /> <structure name="IDR_WEBUI_JS_UTIL" file="js/util.js" type="chrome_html" flattenhtml="true" /> + <structure name="IDR_WEBUI_JS_WEBUI_LISTENER_BEHAVIOR" + 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"> @@ -448,7 +456,9 @@ without changes to the corresponding grd file. --> type="chrome_html" /> </if> <part file="cr_elements_resources.grdp" /> - <part file="polymer_resources.grdp" /> + <if expr="not is_android"> + <part file="polymer_resources.grdp" /> + </if> </structures> </release> </grit> diff --git a/chromium/ui/wm/BUILD.gn b/chromium/ui/wm/BUILD.gn index 858183e7bc1..3be6e2d8d63 100644 --- a/chromium/ui/wm/BUILD.gn +++ b/chromium/ui/wm/BUILD.gn @@ -35,13 +35,6 @@ component("wm") { "core/masked_window_targeter.h", "core/native_cursor_manager.h", "core/native_cursor_manager_delegate.h", - "core/nested_accelerator_controller.cc", - "core/nested_accelerator_controller.h", - "core/nested_accelerator_delegate.h", - "core/nested_accelerator_dispatcher.cc", - "core/nested_accelerator_dispatcher.h", - "core/nested_accelerator_dispatcher_linux.cc", - "core/nested_accelerator_dispatcher_win.cc", "core/shadow.cc", "core/shadow.h", "core/shadow_controller.cc", @@ -93,16 +86,6 @@ component("wm") { if (use_x11) { configs += [ "//build/config/linux:x11" ] } - - if (is_android) { - sources -= [ - "core/nested_accelerator_controller.cc", - "core/nested_accelerator_controller.h", - "core/nested_accelerator_delegate.h", - "core/nested_accelerator_dispatcher.cc", - "core/nested_accelerator_dispatcher.h", - ] - } } static_library("test_support") { @@ -124,15 +107,6 @@ static_library("test_support") { ] } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("wm_unittests_run") { - testonly = true - deps = [ - ":wm_unittests", - ] -} - test("wm_unittests") { sources = [ "core/capture_controller_unittest.cc", @@ -140,7 +114,6 @@ test("wm_unittests") { "core/cursor_manager_unittest.cc", "core/focus_controller_unittest.cc", "core/image_grid_unittest.cc", - "core/nested_accelerator_controller_unittest.cc", "core/shadow_controller_unittest.cc", "core/shadow_unittest.cc", "core/transient_window_manager_unittest.cc", diff --git a/chromium/ui/wm/core/cursor_manager.cc b/chromium/ui/wm/core/cursor_manager.cc index 51a30ac6f20..607614b6131 100644 --- a/chromium/ui/wm/core/cursor_manager.cc +++ b/chromium/ui/wm/core/cursor_manager.cc @@ -73,11 +73,16 @@ class CursorState { } // namespace internal +bool CursorManager::last_cursor_visibility_state_ = true; + CursorManager::CursorManager(scoped_ptr<NativeCursorManager> delegate) : delegate_(std::move(delegate)), cursor_lock_count_(0), current_state_(new internal::CursorState), - state_on_unlock_(new internal::CursorState) {} + state_on_unlock_(new internal::CursorState) { + // Restore the last cursor visibility state. + current_state_->SetVisible(last_cursor_visibility_state_); +} CursorManager::~CursorManager() { } @@ -95,6 +100,7 @@ gfx::NativeCursor CursorManager::GetCursor() const { } void CursorManager::ShowCursor() { + last_cursor_visibility_state_ = true; state_on_unlock_->SetVisible(true); if (cursor_lock_count_ == 0 && IsCursorVisible() != state_on_unlock_->visible()) { @@ -105,6 +111,7 @@ void CursorManager::ShowCursor() { } void CursorManager::HideCursor() { + last_cursor_visibility_state_ = false; state_on_unlock_->SetVisible(false); if (cursor_lock_count_ == 0 && IsCursorVisible() != state_on_unlock_->visible()) { diff --git a/chromium/ui/wm/core/cursor_manager.h b/chromium/ui/wm/core/cursor_manager.h index a3afdd39deb..3e2b86b213d 100644 --- a/chromium/ui/wm/core/cursor_manager.h +++ b/chromium/ui/wm/core/cursor_manager.h @@ -82,6 +82,12 @@ class WM_EXPORT CursorManager : public aura::client::CursorClient, base::ObserverList<aura::client::CursorClientObserver> observers_; + // This flag holds the cursor visibility state for the duration of the + // process. Defaults to true. This flag helps ensure that when a + // CursorManager instance is created it gets populated with the correct + // cursor visibility state. + static bool last_cursor_visibility_state_; + DISALLOW_COPY_AND_ASSIGN(CursorManager); }; diff --git a/chromium/ui/wm/core/cursor_manager_unittest.cc b/chromium/ui/wm/core/cursor_manager_unittest.cc index 7add106969a..e4f5429309a 100644 --- a/chromium/ui/wm/core/cursor_manager_unittest.cc +++ b/chromium/ui/wm/core/cursor_manager_unittest.cc @@ -325,3 +325,38 @@ TEST_F(CursorManagerTest, TestCursorClientObserver) { EXPECT_FALSE(observer_b.did_visibility_change()); EXPECT_TRUE(observer_a.is_cursor_visible()); } + +// This test validates that the cursor visiblity state is restored when a +// CursorManager instance is destroyed and recreated. +TEST(CursorManagerCreateDestroyTest, VisibilityTest) { + // This block ensures that the cursor is hidden when the CursorManager + // instance is destroyed. + { + wm::CursorManager cursor_manager1( + scoped_ptr<wm::NativeCursorManager>(new TestingCursorManager)); + cursor_manager1.ShowCursor(); + EXPECT_TRUE(cursor_manager1.IsCursorVisible()); + cursor_manager1.HideCursor(); + EXPECT_FALSE(cursor_manager1.IsCursorVisible()); + } + + // This block validates that the cursor is hidden initially. It ensures that + // the cursor is visible when the CursorManager instance is destroyed. + { + wm::CursorManager cursor_manager2( + scoped_ptr<wm::NativeCursorManager>(new TestingCursorManager)); + EXPECT_FALSE(cursor_manager2.IsCursorVisible()); + cursor_manager2.ShowCursor(); + EXPECT_TRUE(cursor_manager2.IsCursorVisible()); + } + + // This block validates that the cursor is visible initially. It then + // performs normal cursor visibility operations. + { + wm::CursorManager cursor_manager3( + scoped_ptr<wm::NativeCursorManager>(new TestingCursorManager)); + EXPECT_TRUE(cursor_manager3.IsCursorVisible()); + cursor_manager3.HideCursor(); + EXPECT_FALSE(cursor_manager3.IsCursorVisible()); + } +} diff --git a/chromium/ui/wm/core/default_screen_position_client.cc b/chromium/ui/wm/core/default_screen_position_client.cc index d26a91ed9c8..092b2da6b43 100644 --- a/chromium/ui/wm/core/default_screen_position_client.cc +++ b/chromium/ui/wm/core/default_screen_position_client.cc @@ -7,6 +7,7 @@ #include "ui/aura/window_tree_host.h" #include "ui/gfx/display.h" #include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/screen.h" namespace wm { @@ -19,11 +20,11 @@ DefaultScreenPositionClient::~DefaultScreenPositionClient() { gfx::Point DefaultScreenPositionClient::GetOriginInScreen( const aura::Window* root_window) { - gfx::Point origin_in_pixels = root_window->GetHost()->GetBounds().origin(); aura::Window* window = const_cast<aura::Window*>(root_window); - float scale = gfx::Screen::GetScreenFor(window)-> - GetDisplayNearestWindow(window).device_scale_factor(); - return gfx::ScaleToFlooredPoint(origin_in_pixels, 1.0f / scale); + gfx::Screen* screen = gfx::Screen::GetScreen(); + gfx::Rect screen_bounds = root_window->GetHost()->GetBounds(); + gfx::Rect dip_bounds = screen->ScreenToDIPRectInWindow(window, screen_bounds); + return dip_bounds.origin(); } void DefaultScreenPositionClient::ConvertPointToScreen( diff --git a/chromium/ui/wm/core/image_grid.cc b/chromium/ui/wm/core/image_grid.cc index 553e442d50b..82ca0e3af1e 100644 --- a/chromium/ui/wm/core/image_grid.cc +++ b/chromium/ui/wm/core/image_grid.cc @@ -29,18 +29,20 @@ namespace { // right and bottom layers are stretched to the height or width of the // center image. -void ScaleWidth(gfx::Size center, ui::Layer* layer, gfx::Transform& transform) { +void ScaleWidth(const gfx::Size& center, + ui::Layer* layer, + gfx::Transform* transform) { float layer_width = layer->bounds().width() * layer->device_scale_factor(); float scale = static_cast<float>(center.width()) / layer_width; - transform.Scale(scale, 1.0); + transform->Scale(scale, 1.0); } -void ScaleHeight(gfx::Size center, +void ScaleHeight(const gfx::Size& center, ui::Layer* layer, - gfx::Transform& transform) { + gfx::Transform* transform) { float layer_height = layer->bounds().height() * layer->device_scale_factor(); float scale = static_cast<float>(center.height()) / layer_height; - transform.Scale(1.0, scale); + transform->Scale(1.0, scale); } // Returns the dimensions of |image| if non-NULL or gfx::Size(0, 0) otherwise. @@ -153,7 +155,7 @@ void ImageGrid::SetSize(const gfx::Size& size) { if (center_width > 0) { gfx::Transform transform; transform.Translate(left, 0); - ScaleWidth(center_size_in_pixels, top_layer_.get(), transform); + ScaleWidth(center_size_in_pixels, top_layer_.get(), &transform); top_layer_->SetTransform(transform); } top_layer_->SetVisible(center_width > 0); @@ -163,7 +165,7 @@ void ImageGrid::SetSize(const gfx::Size& size) { gfx::Transform transform; transform.Translate( left, size.height() - bottom_layer_->bounds().height()); - ScaleWidth(center_size_in_pixels, bottom_layer_.get(), transform); + ScaleWidth(center_size_in_pixels, bottom_layer_.get(), &transform); bottom_layer_->SetTransform(transform); } bottom_layer_->SetVisible(center_width > 0); @@ -172,7 +174,7 @@ void ImageGrid::SetSize(const gfx::Size& size) { if (center_height > 0) { gfx::Transform transform; transform.Translate(0, top); - ScaleHeight(center_size_in_pixels, left_layer_.get(), transform); + ScaleHeight(center_size_in_pixels, left_layer_.get(), &transform); left_layer_->SetTransform(transform); } left_layer_->SetVisible(center_height > 0); @@ -182,7 +184,7 @@ void ImageGrid::SetSize(const gfx::Size& size) { gfx::Transform transform; transform.Translate( size.width() - right_layer_->bounds().width(), top); - ScaleHeight(center_size_in_pixels, right_layer_.get(), transform); + ScaleHeight(center_size_in_pixels, right_layer_.get(), &transform); right_layer_->SetTransform(transform); } right_layer_->SetVisible(center_height > 0); @@ -268,7 +270,7 @@ void ImageGrid::ImagePainter::SetClipRect(const gfx::Rect& clip_rect, } void ImageGrid::ImagePainter::OnPaintLayer(const ui::PaintContext& context) { - gfx::Size bounding_size(clip_rect_.right(), clip_rect_.bottom()); + gfx::Size bounding_size(image_.width(), image_.height()); ui::PaintRecorder recorder(context, bounding_size); if (!clip_rect_.IsEmpty()) recorder.canvas()->ClipRect(clip_rect_); @@ -298,8 +300,6 @@ void ImageGrid::SetImage(const gfx::Image* image, const int kMinimumSize = 20; // Clean out old layers and painters. - if (layer_ptr->get()) - layer_->Remove(layer_ptr->get()); layer_ptr->reset(); painter_ptr->reset(); diff --git a/chromium/ui/wm/core/nested_accelerator_controller.cc b/chromium/ui/wm/core/nested_accelerator_controller.cc deleted file mode 100644 index 42ace4e9617..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_controller.cc +++ /dev/null @@ -1,59 +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/nested_accelerator_controller.h" - -#include <utility> - -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/run_loop.h" -#include "ui/wm/core/nested_accelerator_delegate.h" -#include "ui/wm/core/nested_accelerator_dispatcher.h" - -namespace wm { - -NestedAcceleratorController::NestedAcceleratorController( - NestedAcceleratorDelegate* delegate) - : dispatcher_delegate_(delegate) { - DCHECK(delegate); -} - -NestedAcceleratorController::~NestedAcceleratorController() { -} - -void NestedAcceleratorController::PrepareNestedLoopClosures( - base::MessagePumpDispatcher* nested_dispatcher, - base::Closure* run_closure, - base::Closure* quit_closure) { - scoped_ptr<NestedAcceleratorDispatcher> old_accelerator_dispatcher = - std::move(accelerator_dispatcher_); - accelerator_dispatcher_ = NestedAcceleratorDispatcher::Create( - dispatcher_delegate_.get(), nested_dispatcher); - - scoped_ptr<base::RunLoop> run_loop = accelerator_dispatcher_->CreateRunLoop(); - *quit_closure = - base::Bind(&NestedAcceleratorController::QuitNestedMessageLoop, - base::Unretained(this), - run_loop->QuitClosure()); - *run_closure = base::Bind(&NestedAcceleratorController::RunNestedMessageLoop, - base::Unretained(this), - base::Passed(&run_loop), - base::Passed(&old_accelerator_dispatcher)); -} - -void NestedAcceleratorController::RunNestedMessageLoop( - scoped_ptr<base::RunLoop> run_loop, - scoped_ptr<NestedAcceleratorDispatcher> old_accelerator_dispatcher) { - run_loop->Run(); - accelerator_dispatcher_ = std::move(old_accelerator_dispatcher); -} - -void NestedAcceleratorController::QuitNestedMessageLoop( - const base::Closure& quit_runloop) { - quit_runloop.Run(); - accelerator_dispatcher_.reset(); -} - -} // namespace wm diff --git a/chromium/ui/wm/core/nested_accelerator_controller.h b/chromium/ui/wm/core/nested_accelerator_controller.h deleted file mode 100644 index 27cb8de392b..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_controller.h +++ /dev/null @@ -1,47 +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_NESTED_ACCELERATOR_CONTROLLER_H_ -#define UI_WM_CORE_NESTED_ACCELERATOR_CONTROLLER_H_ - -#include "base/callback.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "ui/wm/public/dispatcher_client.h" -#include "ui/wm/wm_export.h" - -namespace wm { - -class NestedAcceleratorDelegate; -class NestedAcceleratorDispatcher; - -// Creates a dispatcher which wraps another dispatcher. -// The outer dispatcher runs first and performs ash specific handling. -// If it does not consume the event it forwards the event to the nested -// dispatcher. -class WM_EXPORT NestedAcceleratorController - : public aura::client::DispatcherClient { - public: - explicit NestedAcceleratorController(NestedAcceleratorDelegate* delegate); - ~NestedAcceleratorController() override; - - // aura::client::DispatcherClient: - void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher, - base::Closure* run_closure, - base::Closure* quit_closure) override; - - private: - void RunNestedMessageLoop(scoped_ptr<base::RunLoop> run_loop, - scoped_ptr<NestedAcceleratorDispatcher> dispatcher); - void QuitNestedMessageLoop(const base::Closure& quit_runloop); - - scoped_ptr<NestedAcceleratorDispatcher> accelerator_dispatcher_; - scoped_ptr<NestedAcceleratorDelegate> dispatcher_delegate_; - - DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorController); -}; - -} // namespace wm - -#endif // UI_WM_CORE_NESTED_ACCELERATOR_CONTROLLER_H_ diff --git a/chromium/ui/wm/core/nested_accelerator_controller_unittest.cc b/chromium/ui/wm/core/nested_accelerator_controller_unittest.cc deleted file mode 100644 index 0266999a046..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_controller_unittest.cc +++ /dev/null @@ -1,210 +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/nested_accelerator_controller.h" - -#include <stdint.h> - -#include "base/bind.h" -#include "base/event_types.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "build/build_config.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/test/test_windows.h" -#include "ui/aura/window.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/base/accelerators/accelerator.h" -#include "ui/base/accelerators/accelerator.h" -#include "ui/base/accelerators/accelerator_manager.h" -#include "ui/events/event_constants.h" -#include "ui/events/event_utils.h" -#include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/events/platform/platform_event_source.h" -#include "ui/events/platform/scoped_event_dispatcher.h" -#include "ui/wm/core/nested_accelerator_delegate.h" -#include "ui/wm/public/dispatcher_client.h" - -#if defined(USE_X11) -#include <X11/Xlib.h> -#include "ui/aura/test/x11_event_sender.h" -#include "ui/events/test/events_test_utils_x11.h" -#endif // USE_X11 - -namespace wm { -namespace test { - -namespace { - -class MockDispatcher : public ui::PlatformEventDispatcher { - public: - MockDispatcher() : num_key_events_dispatched_(0) {} - - int num_key_events_dispatched() { return num_key_events_dispatched_; } - - private: - // ui::PlatformEventDispatcher: - bool CanDispatchEvent(const ui::PlatformEvent& event) override { - return true; - } - uint32_t DispatchEvent(const ui::PlatformEvent& event) override { - if (ui::EventTypeFromNative(event) == ui::ET_KEY_RELEASED) - num_key_events_dispatched_++; - return ui::POST_DISPATCH_NONE; - } - - int num_key_events_dispatched_; - - DISALLOW_COPY_AND_ASSIGN(MockDispatcher); -}; - -class TestTarget : public ui::AcceleratorTarget { - public: - TestTarget() : accelerator_pressed_count_(0) {} - ~TestTarget() override {} - - int accelerator_pressed_count() const { return accelerator_pressed_count_; } - - // Overridden from ui::AcceleratorTarget: - bool AcceleratorPressed(const ui::Accelerator& accelerator) override { - accelerator_pressed_count_++; - return true; - } - bool CanHandleAccelerators() const override { return true; } - - private: - int accelerator_pressed_count_; - - DISALLOW_COPY_AND_ASSIGN(TestTarget); -}; - -void DispatchKeyReleaseA(aura::Window* root_window) { -// Sending both keydown and keyup is necessary here because the accelerator -// manager only checks a keyup event following a keydown event. See -// ShouldHandle() in ui/base/accelerators/accelerator_manager.cc for details. -#if defined(OS_WIN) - aura::WindowTreeHost* host = root_window->GetHost(); - HWND hwnd = host->GetAcceleratedWidget(); - ::PostMessage(hwnd, WM_KEYDOWN, ui::VKEY_A, 0); - ::PostMessage(hwnd, WM_KEYUP, ui::VKEY_A, 0); -#elif defined(USE_X11) - ui::ScopedXI2Event native_event; - native_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, 0); - aura::WindowTreeHost* host = root_window->GetHost(); - aura::test::PostEventToWindowTreeHost(*native_event, host); - native_event.InitKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_A, 0); - aura::test::PostEventToWindowTreeHost(*native_event, host); -#endif - // Make sure the inner message-loop terminates after dispatching the events. - base::MessageLoop::current()->PostTask( - FROM_HERE, base::MessageLoop::current()->QuitWhenIdleClosure()); -} - -class MockNestedAcceleratorDelegate : public NestedAcceleratorDelegate { - public: - MockNestedAcceleratorDelegate() - : accelerator_manager_(new ui::AcceleratorManager) {} - ~MockNestedAcceleratorDelegate() override {} - - // NestedAcceleratorDelegate: - Result ProcessAccelerator(const ui::Accelerator& accelerator) override { - return accelerator_manager_->Process(accelerator) ? - RESULT_PROCESSED : RESULT_NOT_PROCESSED; - } - - void Register(const ui::Accelerator& accelerator, - ui::AcceleratorTarget* target) { - accelerator_manager_->Register( - accelerator, ui::AcceleratorManager::kNormalPriority, target); - } - - private: - scoped_ptr<ui::AcceleratorManager> accelerator_manager_; - - DISALLOW_COPY_AND_ASSIGN(MockNestedAcceleratorDelegate); -}; - -class NestedAcceleratorTest : public aura::test::AuraTestBase { - public: - NestedAcceleratorTest() {} - ~NestedAcceleratorTest() override {} - - void SetUp() override { - AuraTestBase::SetUp(); - delegate_ = new MockNestedAcceleratorDelegate(); - nested_accelerator_controller_.reset( - new NestedAcceleratorController(delegate_)); - aura::client::SetDispatcherClient(root_window(), - nested_accelerator_controller_.get()); - } - - void TearDown() override { - aura::client::SetDispatcherClient(root_window(), NULL); - AuraTestBase::TearDown(); - delegate_ = NULL; - nested_accelerator_controller_.reset(); - } - - MockNestedAcceleratorDelegate* delegate() { return delegate_; } - - private: - scoped_ptr<NestedAcceleratorController> nested_accelerator_controller_; - MockNestedAcceleratorDelegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorTest); -}; - -} // namespace - -// Aura window above lock screen in z order. -// http://crbug.com/396494 -TEST_F(NestedAcceleratorTest, DISABLED_AssociatedWindowAboveLockScreen) { - // TODO(oshima|sadrul): remove when Win implements PES. - if (!ui::PlatformEventSource::GetInstance()) - return; - MockDispatcher inner_dispatcher; - scoped_ptr<aura::Window> mock_lock_container( - CreateNormalWindow(0, root_window(), NULL)); - aura::test::CreateTestWindowWithId(1, mock_lock_container.get()); - - scoped_ptr<aura::Window> associated_window( - CreateNormalWindow(2, root_window(), NULL)); - EXPECT_TRUE(aura::test::WindowIsAbove(associated_window.get(), - mock_lock_container.get())); - - DispatchKeyReleaseA(root_window()); - scoped_ptr<ui::ScopedEventDispatcher> override_dispatcher = - ui::PlatformEventSource::GetInstance()->OverrideDispatcher( - &inner_dispatcher); - aura::client::DispatcherRunLoop run_loop( - aura::client::GetDispatcherClient(root_window()), NULL); - run_loop.Run(); - EXPECT_EQ(1, inner_dispatcher.num_key_events_dispatched()); -} - -// Test that the nested dispatcher handles accelerators. -// http://crbug.com/396494 -TEST_F(NestedAcceleratorTest, DISABLED_AcceleratorsHandled) { - // TODO(oshima|sadrul): remove when Win implements PES. - if (!ui::PlatformEventSource::GetInstance()) - return; - MockDispatcher inner_dispatcher; - ui::Accelerator accelerator(ui::VKEY_A, ui::EF_NONE); - accelerator.set_type(ui::ET_KEY_RELEASED); - TestTarget target; - delegate()->Register(accelerator, &target); - - DispatchKeyReleaseA(root_window()); - scoped_ptr<ui::ScopedEventDispatcher> override_dispatcher = - ui::PlatformEventSource::GetInstance()->OverrideDispatcher( - &inner_dispatcher); - aura::client::DispatcherRunLoop run_loop( - aura::client::GetDispatcherClient(root_window()), NULL); - run_loop.Run(); - EXPECT_EQ(0, inner_dispatcher.num_key_events_dispatched()); - EXPECT_EQ(1, target.accelerator_pressed_count()); -} - -} // namespace test -} // namespace wm diff --git a/chromium/ui/wm/core/nested_accelerator_delegate.h b/chromium/ui/wm/core/nested_accelerator_delegate.h deleted file mode 100644 index 4b13c0d882b..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_delegate.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. - -#ifndef UI_WM_CORE_NESTED_ACCELERATOR_DELEGATE_H_ -#define UI_WM_CORE_NESTED_ACCELERATOR_DELEGATE_H_ - -namespace ui { -class Accelerator; -} - -namespace wm { - -// A delegate interface that implements the behavior of nested accelerator -// handling. -class NestedAcceleratorDelegate { - public: - enum Result { - RESULT_PROCESSED, - RESULT_NOT_PROCESSED, - // The key event should be ignored now and instead be reposted so that - // next event loop. - RESULT_PROCESS_LATER, - }; - - virtual ~NestedAcceleratorDelegate() {} - - // Attempts to process the |accelerator|. - virtual Result ProcessAccelerator(const ui::Accelerator& accelerator) = 0; -}; - -} // namespace wm - -#endif // UI_WM_CORE_NESTED_ACCELERATOR_DELEGATE_H_ diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher.cc b/chromium/ui/wm/core/nested_accelerator_dispatcher.cc deleted file mode 100644 index d37c93c796f..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_dispatcher.cc +++ /dev/null @@ -1,21 +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/nested_accelerator_dispatcher.h" - -#include "base/logging.h" -#include "ui/wm/core/nested_accelerator_delegate.h" - -namespace wm { - -NestedAcceleratorDispatcher::NestedAcceleratorDispatcher( - NestedAcceleratorDelegate* delegate) - : delegate_(delegate) { - DCHECK(delegate); -} - -NestedAcceleratorDispatcher::~NestedAcceleratorDispatcher() { -} - -} // namespace wm diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher.h b/chromium/ui/wm/core/nested_accelerator_dispatcher.h deleted file mode 100644 index df5dd0862d9..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_dispatcher.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_WM_CORE_NESTED_ACCELERATOR_DISPATCHER_H_ -#define UI_WM_CORE_NESTED_ACCELERATOR_DISPATCHER_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "ui/wm/wm_export.h" - -namespace base { -class MessagePumpDispatcher; -class RunLoop; -} - -namespace ui { -class KeyEvent; -} - -namespace wm { - -class NestedAcceleratorDelegate; - -// Dispatcher for handling accelerators from menu. -// -// Wraps a nested dispatcher to which control is passed if no accelerator key -// has been pressed. If the nested dispatcher is NULL, then the control is -// passed back to the default dispatcher. -// TODO(pkotwicz): Add support for a |nested_dispatcher| which sends -// events to a system IME. -class WM_EXPORT NestedAcceleratorDispatcher { - public: - virtual ~NestedAcceleratorDispatcher(); - - static scoped_ptr<NestedAcceleratorDispatcher> Create( - NestedAcceleratorDelegate* dispatcher_delegate, - base::MessagePumpDispatcher* nested_dispatcher); - - // Creates a base::RunLoop object to run a nested message loop. - virtual scoped_ptr<base::RunLoop> CreateRunLoop() = 0; - - protected: - explicit NestedAcceleratorDispatcher(NestedAcceleratorDelegate* delegate); - - NestedAcceleratorDelegate* - delegate_; // Owned by NestedAcceleratorController. - - private: - DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorDispatcher); -}; - -} // namespace wm - -#endif // UI_WM_CORE_NESTED_ACCELERATOR_DISPATCHER_H_ diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher_linux.cc b/chromium/ui/wm/core/nested_accelerator_dispatcher_linux.cc deleted file mode 100644 index d9060041d4f..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_dispatcher_linux.cc +++ /dev/null @@ -1,104 +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/nested_accelerator_dispatcher.h" - -#include <stdint.h> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/run_loop.h" -#include "ui/base/accelerators/accelerator.h" -#include "ui/events/event.h" -#include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/events/platform/platform_event_source.h" -#include "ui/events/platform/scoped_event_dispatcher.h" -#include "ui/wm/core/accelerator_filter.h" -#include "ui/wm/core/nested_accelerator_delegate.h" - -#if defined(USE_X11) -#include <X11/Xlib.h> -#endif - -namespace wm { - -namespace { - -#if defined(USE_OZONE) -bool IsKeyEvent(const base::NativeEvent& native_event) { - const ui::KeyEvent* event = static_cast<const ui::KeyEvent*>(native_event); - return event->IsKeyEvent(); -} -#elif defined(USE_X11) -bool IsKeyEvent(const XEvent* xev) { - return xev->type == KeyPress || xev->type == KeyRelease; -} -#else -#error Unknown build platform: you should have either use_ozone or use_x11. -#endif - -scoped_ptr<ui::ScopedEventDispatcher> OverrideDispatcher( - ui::PlatformEventDispatcher* dispatcher) { - ui::PlatformEventSource* source = ui::PlatformEventSource::GetInstance(); - return source ? source->OverrideDispatcher(dispatcher) : nullptr; -} - -} // namespace - -class NestedAcceleratorDispatcherLinux : public NestedAcceleratorDispatcher, - public ui::PlatformEventDispatcher { - public: - explicit NestedAcceleratorDispatcherLinux(NestedAcceleratorDelegate* delegate) - : NestedAcceleratorDispatcher(delegate), - restore_dispatcher_(OverrideDispatcher(this)) {} - - ~NestedAcceleratorDispatcherLinux() override {} - - private: - // AcceleratorDispatcher: - scoped_ptr<base::RunLoop> CreateRunLoop() override { - return make_scoped_ptr(new base::RunLoop()); - } - - // ui::PlatformEventDispatcher: - bool CanDispatchEvent(const ui::PlatformEvent& event) override { - return true; - } - - uint32_t DispatchEvent(const ui::PlatformEvent& event) override { - if (IsKeyEvent(event)) { - ui::Accelerator accelerator((ui::KeyEvent(event))); - - switch (delegate_->ProcessAccelerator(accelerator)) { - case NestedAcceleratorDelegate::RESULT_PROCESS_LATER: -#if defined(USE_X11) - XPutBackEvent(event->xany.display, event); -#else - NOTIMPLEMENTED(); -#endif - return ui::POST_DISPATCH_NONE; - case NestedAcceleratorDelegate::RESULT_PROCESSED: - return ui::POST_DISPATCH_NONE; - case NestedAcceleratorDelegate::RESULT_NOT_PROCESSED: - break; - } - } - ui::PlatformEventDispatcher* prev = *restore_dispatcher_; - - uint32_t perform_default = ui::POST_DISPATCH_PERFORM_DEFAULT; - return prev ? prev->DispatchEvent(event) : perform_default; - } - - scoped_ptr<ui::ScopedEventDispatcher> restore_dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorDispatcherLinux); -}; - -scoped_ptr<NestedAcceleratorDispatcher> NestedAcceleratorDispatcher::Create( - NestedAcceleratorDelegate* delegate, - base::MessagePumpDispatcher* nested_dispatcher) { - return make_scoped_ptr(new NestedAcceleratorDispatcherLinux(delegate)); -} - -} // namespace wm diff --git a/chromium/ui/wm/core/nested_accelerator_dispatcher_win.cc b/chromium/ui/wm/core/nested_accelerator_dispatcher_win.cc deleted file mode 100644 index 1b59d689017..00000000000 --- a/chromium/ui/wm/core/nested_accelerator_dispatcher_win.cc +++ /dev/null @@ -1,76 +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/nested_accelerator_dispatcher.h" - -#include <stdint.h> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_pump_dispatcher.h" -#include "base/run_loop.h" -#include "ui/base/accelerators/accelerator.h" -#include "ui/events/event.h" -#include "ui/wm/core/accelerator_filter.h" -#include "ui/wm/core/nested_accelerator_delegate.h" - -using base::MessagePumpDispatcher; - -namespace wm { - -namespace { - -bool IsKeyEvent(const MSG& msg) { - return msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN || - msg.message == WM_KEYUP || msg.message == WM_SYSKEYUP; -} - -} // namespace - -class NestedAcceleratorDispatcherWin : public NestedAcceleratorDispatcher, - public MessagePumpDispatcher { - public: - NestedAcceleratorDispatcherWin(NestedAcceleratorDelegate* delegate, - MessagePumpDispatcher* nested) - : NestedAcceleratorDispatcher(delegate), nested_dispatcher_(nested) {} - ~NestedAcceleratorDispatcherWin() override {} - - private: - // NestedAcceleratorDispatcher: - scoped_ptr<base::RunLoop> CreateRunLoop() override { - return make_scoped_ptr(new base::RunLoop(this)); - } - - // MessagePumpDispatcher: - uint32_t Dispatch(const MSG& event) override { - if (IsKeyEvent(event)) { - ui::Accelerator accelerator((ui::KeyEvent(event))); - - switch (delegate_->ProcessAccelerator(accelerator)) { - case NestedAcceleratorDelegate::RESULT_PROCESS_LATER: - return POST_DISPATCH_QUIT_LOOP; - case NestedAcceleratorDelegate::RESULT_PROCESSED: - return POST_DISPATCH_NONE; - case NestedAcceleratorDelegate::RESULT_NOT_PROCESSED: - break; - } - } - - return nested_dispatcher_ ? nested_dispatcher_->Dispatch(event) - : POST_DISPATCH_PERFORM_DEFAULT; - } - - MessagePumpDispatcher* nested_dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(NestedAcceleratorDispatcherWin); -}; - -scoped_ptr<NestedAcceleratorDispatcher> NestedAcceleratorDispatcher::Create( - NestedAcceleratorDelegate* delegate, - MessagePumpDispatcher* nested_dispatcher) { - return make_scoped_ptr( - new NestedAcceleratorDispatcherWin(delegate, nested_dispatcher)); -} - -} // namespace wm diff --git a/chromium/ui/wm/core/shadow_unittest.cc b/chromium/ui/wm/core/shadow_unittest.cc index 14236874cab..f69095e5133 100644 --- a/chromium/ui/wm/core/shadow_unittest.cc +++ b/chromium/ui/wm/core/shadow_unittest.cc @@ -60,8 +60,7 @@ class MockResourceBundleDelegate : public ui::ResourceBundle::Delegate { return gfx::Image(); } } - gfx::Image GetNativeImageNamed(int resource_id, - ui::ResourceBundle::ImageRTL rtl) override { + gfx::Image GetNativeImageNamed(int resource_id) override { return gfx::Image(); } base::RefCountedStaticMemory* LoadDataResourceBytes( @@ -77,9 +76,6 @@ class MockResourceBundleDelegate : public ui::ResourceBundle::Delegate { bool GetLocalizedString(int message_id, base::string16* value) override { return false; } - scoped_ptr<gfx::Font> GetFont(ui::ResourceBundle::FontStyle style) override { - return nullptr; - } int last_resource_id() const { return last_resource_id_; } diff --git a/chromium/ui/wm/public/dispatcher_client.cc b/chromium/ui/wm/public/dispatcher_client.cc deleted file mode 100644 index e3be7ca848b..00000000000 --- a/chromium/ui/wm/public/dispatcher_client.cc +++ /dev/null @@ -1,52 +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/wm/public/dispatcher_client.h" - -#include "base/callback.h" -#include "ui/aura/window.h" -#include "ui/aura/window_property.h" - -DECLARE_WINDOW_PROPERTY_TYPE(aura::client::DispatcherClient*); - -namespace aura { -namespace client { - -DispatcherRunLoop::DispatcherRunLoop(DispatcherClient* client, - base::MessagePumpDispatcher* dispatcher) { - client->PrepareNestedLoopClosures(dispatcher, &run_closure_, &quit_closure_); -} - -DispatcherRunLoop::~DispatcherRunLoop() { -} - -void DispatcherRunLoop::Run() { - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoopForUI::ScopedNestableTaskAllower allow_nested(loop); - run_closure_.Run(); -} - -base::Closure DispatcherRunLoop::QuitClosure() { - return quit_closure_; -} - -void DispatcherRunLoop::Quit() { - quit_closure_.Run(); -} - -DEFINE_LOCAL_WINDOW_PROPERTY_KEY(DispatcherClient*, kDispatcherClientKey, NULL); - -void SetDispatcherClient(Window* root_window, DispatcherClient* client) { - DCHECK_EQ(root_window->GetRootWindow(), root_window); - root_window->SetProperty(kDispatcherClientKey, client); -} - -DispatcherClient* GetDispatcherClient(Window* root_window) { - if (root_window) - DCHECK_EQ(root_window->GetRootWindow(), root_window); - return root_window ? root_window->GetProperty(kDispatcherClientKey) : NULL; -} - -} // namespace client -} // namespace aura diff --git a/chromium/ui/wm/public/dispatcher_client.h b/chromium/ui/wm/public/dispatcher_client.h deleted file mode 100644 index a301e92dd55..00000000000 --- a/chromium/ui/wm/public/dispatcher_client.h +++ /dev/null @@ -1,59 +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_WM_PUBLIC_DISPATCHER_CLIENT_H_ -#define UI_WM_PUBLIC_DISPATCHER_CLIENT_H_ - -#include "base/callback.h" -#include "base/macros.h" -#include "base/message_loop/message_pump_dispatcher.h" -#include "ui/aura/aura_export.h" - -namespace aura { -class Window; -namespace client { - -class DispatcherClient; - -// A base::RunLoop like object for running a nested message-loop with a -// specified DispatcherClient and a MessagePumpDispatcher. -class AURA_EXPORT DispatcherRunLoop { - public: - DispatcherRunLoop(DispatcherClient* client, - base::MessagePumpDispatcher* dispatcher); - ~DispatcherRunLoop(); - - void Run(); - base::Closure QuitClosure(); - void Quit(); - - private: - base::Closure run_closure_; - base::Closure quit_closure_; - - DISALLOW_COPY_AND_ASSIGN(DispatcherRunLoop); -}; - -// An interface implemented by an object which handles nested dispatchers. -class AURA_EXPORT DispatcherClient { - public: - virtual ~DispatcherClient() {} - - protected: - friend class DispatcherRunLoop; - - virtual void PrepareNestedLoopClosures( - base::MessagePumpDispatcher* dispatcher, - base::Closure* run_closure, - base::Closure* quit_closure) = 0; -}; - -AURA_EXPORT void SetDispatcherClient(Window* root_window, - DispatcherClient* client); -AURA_EXPORT DispatcherClient* GetDispatcherClient(Window* root_window); - -} // namespace client -} // namespace aura - -#endif // UI_WM_PUBLIC_DISPATCHER_CLIENT_H_ diff --git a/chromium/ui/wm/public/tooltip_client.h b/chromium/ui/wm/public/tooltip_client.h index f58a63e12a6..77095153b6e 100644 --- a/chromium/ui/wm/public/tooltip_client.h +++ b/chromium/ui/wm/public/tooltip_client.h @@ -21,8 +21,7 @@ class ScopedTooltipDisabler; class AURA_EXPORT TooltipClient { public: // Returns the max width of the tooltip when shown at the specified location. - virtual int GetMaxWidth(const gfx::Point& point, - aura::Window* context) const = 0; + virtual int GetMaxWidth(const gfx::Point& point) const = 0; // Informs the shell tooltip manager of change in tooltip for window |target|. virtual void UpdateTooltip(Window* target) = 0; diff --git a/chromium/ui/wm/wm.gyp b/chromium/ui/wm/wm.gyp index 28a8cb28f4e..685c9769add 100644 --- a/chromium/ui/wm/wm.gyp +++ b/chromium/ui/wm/wm.gyp @@ -60,13 +60,6 @@ 'core/masked_window_targeter.h', 'core/native_cursor_manager.h', 'core/native_cursor_manager_delegate.h', - 'core/nested_accelerator_controller.cc', - 'core/nested_accelerator_controller.h', - 'core/nested_accelerator_delegate.h', - 'core/nested_accelerator_dispatcher.cc', - 'core/nested_accelerator_dispatcher.h', - 'core/nested_accelerator_dispatcher_linux.cc', - 'core/nested_accelerator_dispatcher_win.cc', 'core/shadow.cc', 'core/shadow.h', 'core/shadow_controller.cc', @@ -100,15 +93,6 @@ '../../build/linux/system.gyp:x11', ], }], - ['OS=="android"', { - 'sources!': [ - 'core/nested_accelerator_controller.cc', - 'core/nested_accelerator_controller.h', - 'core/nested_accelerator_delegate.h', - 'core/nested_accelerator_dispatcher.cc', - 'core/nested_accelerator_dispatcher.h', - ], - }], ], }, { @@ -154,7 +138,6 @@ 'core/cursor_manager_unittest.cc', 'core/focus_controller_unittest.cc', 'core/image_grid_unittest.cc', - 'core/nested_accelerator_controller_unittest.cc', 'core/shadow_controller_unittest.cc', 'core/shadow_unittest.cc', 'core/transient_window_manager_unittest.cc', |