Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <set> | 5 #include <set> |
| 6 | 6 |
| 7 #include "ash/common/accelerators/accelerator_table.h" | 7 #include "ash/common/accelerators/accelerator_table.h" |
| 8 #include "base/strings/string_util.h" | 8 #include "base/strings/string_util.h" |
| 9 #include "testing/gtest/include/gtest/gtest.h" | 9 #include "testing/gtest/include/gtest/gtest.h" |
| 10 | 10 |
| 11 namespace ash { | 11 namespace ash { |
| 12 | 12 |
| 13 namespace { | 13 namespace { |
| 14 | 14 |
| 15 struct Cmp { | 15 struct Cmp { |
| 16 bool operator()(const AcceleratorData& lhs, const AcceleratorData& rhs) { | 16 bool operator()(const AcceleratorData& lhs, const AcceleratorData& rhs) { |
| 17 if (lhs.trigger_on_press != rhs.trigger_on_press) | 17 if (lhs.trigger_on_press != rhs.trigger_on_press) |
| 18 return lhs.trigger_on_press < rhs.trigger_on_press; | 18 return lhs.trigger_on_press < rhs.trigger_on_press; |
| 19 if (lhs.keycode != rhs.keycode) | 19 if (lhs.keycode != rhs.keycode) |
| 20 return lhs.keycode < rhs.keycode; | 20 return lhs.keycode < rhs.keycode; |
| 21 return lhs.modifiers < rhs.modifiers; | 21 return lhs.modifiers < rhs.modifiers; |
| 22 // Do not check |action|. | 22 // Do not check |action|. |
| 23 } | 23 } |
| 24 }; | 24 }; |
| 25 | 25 |
| 26 const int kDebugModifier = | |
|
Daniel Erat
2017/04/05 23:32:51
this looks like it's only used in one place. since
wutao
2017/04/06 17:38:51
Changed method to number and hash based, so do not
| |
| 27 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN; | |
| 28 | |
| 29 // This table snapshot was added on 2017-04-05 to white list these | |
| 30 // accelerators having no Search in their modifiers. Please do not modify this | |
| 31 // table. All new accelerators must base on Search and should be approved by UX. | |
| 32 const AcceleratorData kAcceleratorData_WhiteList20170405[] = { | |
| 33 {true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, PREVIOUS_IME}, | |
| 34 {false, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, PREVIOUS_IME}, | |
| 35 {true, ui::VKEY_CONVERT, ui::EF_NONE, SWITCH_IME}, | |
| 36 {true, ui::VKEY_NONCONVERT, ui::EF_NONE, SWITCH_IME}, | |
| 37 {true, ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE, SWITCH_IME}, | |
| 38 {true, ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE, SWITCH_IME}, | |
| 39 {true, ui::VKEY_HANGUL, ui::EF_NONE, SWITCH_IME}, | |
| 40 {true, ui::VKEY_TAB, ui::EF_ALT_DOWN, CYCLE_FORWARD_MRU}, | |
| 41 {true, ui::VKEY_TAB, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, | |
| 42 CYCLE_BACKWARD_MRU}, | |
| 43 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_NONE, TOGGLE_OVERVIEW}, | |
| 44 {true, ui::VKEY_BROWSER_SEARCH, ui::EF_NONE, TOGGLE_APP_LIST}, | |
| 45 {true, ui::VKEY_WLAN, ui::EF_NONE, TOGGLE_WIFI}, | |
| 46 {true, ui::VKEY_KBD_BRIGHTNESS_DOWN, ui::EF_NONE, KEYBOARD_BRIGHTNESS_DOWN}, | |
| 47 {true, ui::VKEY_KBD_BRIGHTNESS_UP, ui::EF_NONE, KEYBOARD_BRIGHTNESS_UP}, | |
| 48 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_CONTROL_DOWN, TOGGLE_MIRROR_MODE}, | |
| 49 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_ALT_DOWN, SWAP_PRIMARY_DISPLAY}, | |
| 50 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_CONTROL_DOWN, TAKE_SCREENSHOT}, | |
| 51 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, | |
| 52 TAKE_PARTIAL_SCREENSHOT}, | |
| 53 {true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
| 54 TAKE_WINDOW_SCREENSHOT}, | |
| 55 {true, ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE, BRIGHTNESS_DOWN}, | |
| 56 {true, ui::VKEY_BRIGHTNESS_DOWN, ui::EF_ALT_DOWN, KEYBOARD_BRIGHTNESS_DOWN}, | |
| 57 {true, ui::VKEY_BRIGHTNESS_UP, ui::EF_NONE, BRIGHTNESS_UP}, | |
| 58 {true, ui::VKEY_BRIGHTNESS_UP, ui::EF_ALT_DOWN, KEYBOARD_BRIGHTNESS_UP}, | |
| 59 {true, ui::VKEY_BRIGHTNESS_DOWN, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 60 MAGNIFY_SCREEN_ZOOM_OUT}, | |
| 61 {true, ui::VKEY_BRIGHTNESS_UP, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 62 MAGNIFY_SCREEN_ZOOM_IN}, | |
| 63 {true, ui::VKEY_F13, ui::EF_NONE, LOCK_PRESSED}, | |
| 64 {false, ui::VKEY_F13, ui::EF_NONE, LOCK_RELEASED}, | |
| 65 {true, ui::VKEY_SLEEP, ui::EF_NONE, LOCK_PRESSED}, | |
| 66 {false, ui::VKEY_SLEEP, ui::EF_NONE, LOCK_RELEASED}, | |
| 67 {true, ui::VKEY_POWER, ui::EF_NONE, POWER_PRESSED}, | |
| 68 {false, ui::VKEY_POWER, ui::EF_NONE, POWER_RELEASED}, | |
| 69 {true, ui::VKEY_M, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, OPEN_FILE_MANAGER}, | |
| 70 {true, ui::VKEY_OEM_2, ui::EF_CONTROL_DOWN, OPEN_GET_HELP}, | |
| 71 {true, ui::VKEY_OEM_2, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, | |
| 72 OPEN_GET_HELP}, | |
| 73 {true, ui::VKEY_T, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, OPEN_CROSH}, | |
| 74 {true, ui::VKEY_I, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 75 TOUCH_HUD_MODE_CHANGE}, | |
| 76 {true, ui::VKEY_I, | |
| 77 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN, | |
| 78 TOUCH_HUD_CLEAR}, | |
| 79 {true, ui::VKEY_P, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 80 TOUCH_HUD_PROJECTION_TOGGLE}, | |
| 81 {true, ui::VKEY_Z, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 82 TOGGLE_SPOKEN_FEEDBACK}, | |
| 83 {true, ui::VKEY_OEM_COMMA, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 84 SWITCH_TO_PREVIOUS_USER}, | |
| 85 {true, ui::VKEY_OEM_PERIOD, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 86 SWITCH_TO_NEXT_USER}, | |
| 87 {false, ui::VKEY_LSHIFT, ui::EF_NONE, DISABLE_CAPS_LOCK}, | |
| 88 {false, ui::VKEY_SHIFT, ui::EF_NONE, DISABLE_CAPS_LOCK}, | |
| 89 {false, ui::VKEY_RSHIFT, ui::EF_NONE, DISABLE_CAPS_LOCK}, | |
| 90 {false, ui::VKEY_LWIN, ui::EF_ALT_DOWN, TOGGLE_CAPS_LOCK}, | |
| 91 {true, ui::VKEY_VOLUME_MUTE, ui::EF_NONE, VOLUME_MUTE}, | |
| 92 {true, ui::VKEY_VOLUME_DOWN, ui::EF_NONE, VOLUME_DOWN}, | |
| 93 {true, ui::VKEY_VOLUME_UP, ui::EF_NONE, VOLUME_UP}, | |
| 94 {true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, NEXT_IME}, | |
| 95 {true, ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, OPEN_FEEDBACK_PAGE}, | |
| 96 {true, ui::VKEY_Q, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, EXIT}, | |
| 97 {true, ui::VKEY_N, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, | |
| 98 NEW_INCOGNITO_WINDOW}, | |
| 99 {true, ui::VKEY_N, ui::EF_CONTROL_DOWN, NEW_WINDOW}, | |
| 100 {true, ui::VKEY_T, ui::EF_CONTROL_DOWN, NEW_TAB}, | |
| 101 {true, ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, | |
| 102 SCALE_UI_UP}, | |
| 103 {true, ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, | |
| 104 SCALE_UI_DOWN}, | |
| 105 {true, ui::VKEY_0, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, SCALE_UI_RESET}, | |
| 106 {true, ui::VKEY_BROWSER_REFRESH, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, | |
| 107 ROTATE_SCREEN}, | |
| 108 {true, ui::VKEY_BROWSER_REFRESH, | |
| 109 ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, ROTATE_WINDOW}, | |
| 110 {true, ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, RESTORE_TAB}, | |
| 111 {true, ui::VKEY_PRINT, ui::EF_NONE, TAKE_SCREENSHOT}, | |
| 112 {false, ui::VKEY_LWIN, ui::EF_NONE, TOGGLE_APP_LIST}, | |
| 113 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_NONE, TOGGLE_FULLSCREEN}, | |
| 114 {true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_SHIFT_DOWN, TOGGLE_FULLSCREEN}, | |
| 115 {true, ui::VKEY_L, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, FOCUS_SHELF}, | |
| 116 {true, ui::VKEY_HELP, ui::EF_NONE, SHOW_KEYBOARD_OVERLAY}, | |
| 117 {true, ui::VKEY_OEM_2, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 118 SHOW_KEYBOARD_OVERLAY}, | |
| 119 {true, ui::VKEY_OEM_2, | |
| 120 ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, | |
| 121 SHOW_KEYBOARD_OVERLAY}, | |
| 122 {true, ui::VKEY_F14, ui::EF_NONE, SHOW_KEYBOARD_OVERLAY}, | |
| 123 {true, ui::VKEY_N, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, | |
| 124 SHOW_MESSAGE_CENTER_BUBBLE}, | |
| 125 {true, ui::VKEY_P, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, SHOW_STYLUS_TOOLS}, | |
| 126 {true, ui::VKEY_S, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, | |
| 127 SHOW_SYSTEM_TRAY_BUBBLE}, | |
| 128 {true, ui::VKEY_1, ui::EF_ALT_DOWN, LAUNCH_APP_0}, | |
| 129 {true, ui::VKEY_2, ui::EF_ALT_DOWN, LAUNCH_APP_1}, | |
| 130 {true, ui::VKEY_3, ui::EF_ALT_DOWN, LAUNCH_APP_2}, | |
| 131 {true, ui::VKEY_4, ui::EF_ALT_DOWN, LAUNCH_APP_3}, | |
| 132 {true, ui::VKEY_5, ui::EF_ALT_DOWN, LAUNCH_APP_4}, | |
| 133 {true, ui::VKEY_6, ui::EF_ALT_DOWN, LAUNCH_APP_5}, | |
| 134 {true, ui::VKEY_7, ui::EF_ALT_DOWN, LAUNCH_APP_6}, | |
| 135 {true, ui::VKEY_8, ui::EF_ALT_DOWN, LAUNCH_APP_7}, | |
| 136 {true, ui::VKEY_9, ui::EF_ALT_DOWN, LAUNCH_LAST_APP}, | |
| 137 {true, ui::VKEY_OEM_4, ui::EF_ALT_DOWN, WINDOW_CYCLE_SNAP_LEFT}, | |
| 138 {true, ui::VKEY_OEM_6, ui::EF_ALT_DOWN, WINDOW_CYCLE_SNAP_RIGHT}, | |
| 139 {true, ui::VKEY_OEM_MINUS, ui::EF_ALT_DOWN, WINDOW_MINIMIZE}, | |
| 140 {true, ui::VKEY_OEM_PLUS, ui::EF_ALT_DOWN, TOGGLE_MAXIMIZED}, | |
| 141 {true, ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, | |
| 142 WINDOW_POSITION_CENTER}, | |
| 143 {true, ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN, FOCUS_NEXT_PANE}, | |
| 144 {true, ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN, FOCUS_PREVIOUS_PANE}, | |
| 145 {true, ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE, MEDIA_NEXT_TRACK}, | |
| 146 {true, ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE, MEDIA_PLAY_PAUSE}, | |
| 147 {true, ui::VKEY_MEDIA_PREV_TRACK, ui::EF_NONE, MEDIA_PREV_TRACK}, | |
| 148 {true, ui::VKEY_U, kDebugModifier, PRINT_UI_HIERARCHIES}, | |
| 149 }; | |
| 150 | |
| 151 const size_t kAcceleratorDataWhiteListLength = | |
| 152 arraysize(kAcceleratorData_WhiteList20170405); | |
| 153 | |
| 26 } // namespace | 154 } // namespace |
| 27 | 155 |
| 28 TEST(AcceleratorTableTest, CheckDuplicatedAccelerators) { | 156 TEST(AcceleratorTableTest, CheckDuplicatedAccelerators) { |
| 29 std::set<AcceleratorData, Cmp> accelerators; | 157 std::set<AcceleratorData, Cmp> accelerators; |
| 30 for (size_t i = 0; i < kAcceleratorDataLength; ++i) { | 158 for (size_t i = 0; i < kAcceleratorDataLength; ++i) { |
| 31 const AcceleratorData& entry = kAcceleratorData[i]; | 159 const AcceleratorData& entry = kAcceleratorData[i]; |
| 32 EXPECT_TRUE(accelerators.insert(entry).second) | 160 EXPECT_TRUE(accelerators.insert(entry).second) |
| 33 << "Duplicated accelerator: " << entry.trigger_on_press << ", " | 161 << "Duplicated accelerator:" |
| 34 << entry.keycode << ", " << (entry.modifiers & ui::EF_SHIFT_DOWN) | 162 << " trigger_on_press=" << entry.trigger_on_press |
|
Daniel Erat
2017/04/05 23:32:51
since this logging code appears multiple times, ho
wutao
2017/04/06 17:38:51
Done.
| |
| 35 << ", " << (entry.modifiers & ui::EF_CONTROL_DOWN) << ", " | 163 << " keycode=" << entry.keycode |
| 36 << (entry.modifiers & ui::EF_ALT_DOWN); | 164 << " SEARCH=" << (entry.modifiers & ui::EF_COMMAND_DOWN) |
| 165 << " SHIFT=" << (entry.modifiers & ui::EF_SHIFT_DOWN) | |
| 166 << " CONTROL=" << (entry.modifiers & ui::EF_CONTROL_DOWN) | |
| 167 << " ALT=" << (entry.modifiers & ui::EF_ALT_DOWN); | |
| 37 } | 168 } |
| 38 } | 169 } |
| 39 | 170 |
| 40 TEST(AcceleratorTableTest, CheckDuplicatedReservedActions) { | 171 TEST(AcceleratorTableTest, CheckDuplicatedReservedActions) { |
| 41 std::set<AcceleratorAction> actions; | 172 std::set<AcceleratorAction> actions; |
| 42 for (size_t i = 0; i < kReservedActionsLength; ++i) { | 173 for (size_t i = 0; i < kReservedActionsLength; ++i) { |
| 43 EXPECT_TRUE(actions.insert(kReservedActions[i]).second) | 174 EXPECT_TRUE(actions.insert(kReservedActions[i]).second) |
| 44 << "Duplicated action: " << kReservedActions[i]; | 175 << "Duplicated action: " << kReservedActions[i]; |
| 45 } | 176 } |
| 46 } | 177 } |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 73 << "Duplicated action: " << kRepeatableActions[i] << " at index: " << i; | 204 << "Duplicated action: " << kRepeatableActions[i] << " at index: " << i; |
| 74 } | 205 } |
| 75 } | 206 } |
| 76 | 207 |
| 77 TEST(AcceleratorTableTest, CheckDeprecatedAccelerators) { | 208 TEST(AcceleratorTableTest, CheckDeprecatedAccelerators) { |
| 78 std::set<AcceleratorData, Cmp> deprecated_actions; | 209 std::set<AcceleratorData, Cmp> deprecated_actions; |
| 79 for (size_t i = 0; i < kDeprecatedAcceleratorsLength; ++i) { | 210 for (size_t i = 0; i < kDeprecatedAcceleratorsLength; ++i) { |
| 80 // A deprecated action can never appear twice in the list. | 211 // A deprecated action can never appear twice in the list. |
| 81 const AcceleratorData& entry = kDeprecatedAccelerators[i]; | 212 const AcceleratorData& entry = kDeprecatedAccelerators[i]; |
| 82 EXPECT_TRUE(deprecated_actions.insert(entry).second) | 213 EXPECT_TRUE(deprecated_actions.insert(entry).second) |
| 83 << "Duplicate deprecated accelerator: " << entry.trigger_on_press | 214 << "Duplicate deprecated accelerator:" |
| 84 << ", " << entry.keycode << ", " | 215 << " trigger_on_press=" << entry.trigger_on_press |
| 85 << (entry.modifiers & ui::EF_SHIFT_DOWN) << ", " | 216 << " keycode=" << entry.keycode |
| 86 << (entry.modifiers & ui::EF_CONTROL_DOWN) << ", " | 217 << " SEARCH=" << (entry.modifiers & ui::EF_COMMAND_DOWN) |
| 87 << (entry.modifiers & ui::EF_ALT_DOWN); | 218 << " SHIFT=" << (entry.modifiers & ui::EF_SHIFT_DOWN) |
| 219 << " CONTROL=" << (entry.modifiers & ui::EF_CONTROL_DOWN) | |
| 220 << " ALT=" << (entry.modifiers & ui::EF_ALT_DOWN); | |
| 88 } | 221 } |
| 89 | 222 |
| 90 std::set<AcceleratorAction> actions; | 223 std::set<AcceleratorAction> actions; |
| 91 for (size_t i = 0; i < kDeprecatedAcceleratorsDataLength; ++i) { | 224 for (size_t i = 0; i < kDeprecatedAcceleratorsDataLength; ++i) { |
| 92 // There must never be any duplicated actions. | 225 // There must never be any duplicated actions. |
| 93 const DeprecatedAcceleratorData& data = kDeprecatedAcceleratorsData[i]; | 226 const DeprecatedAcceleratorData& data = kDeprecatedAcceleratorsData[i]; |
| 94 EXPECT_TRUE(actions.insert(data.action).second) << "Deprecated action: " | 227 EXPECT_TRUE(actions.insert(data.action).second) << "Deprecated action: " |
| 95 << data.action; | 228 << data.action; |
| 96 | 229 |
| 97 // The UMA histogram name must be of the format "Ash.Accelerators.*" | 230 // The UMA histogram name must be of the format "Ash.Accelerators.*" |
| 98 std::string uma_histogram(data.uma_histogram_name); | 231 std::string uma_histogram(data.uma_histogram_name); |
| 99 EXPECT_TRUE(base::StartsWith(uma_histogram, "Ash.Accelerators.", | 232 EXPECT_TRUE(base::StartsWith(uma_histogram, "Ash.Accelerators.", |
| 100 base::CompareCase::SENSITIVE)); | 233 base::CompareCase::SENSITIVE)); |
| 101 } | 234 } |
| 102 } | 235 } |
| 103 | 236 |
| 237 // If this test fails, please change the accelerator modifiers to include the | |
| 238 // Search key. All new accelerators should be approved by UX. | |
| 239 TEST(AcceleratorTableTest, CheckSearchBasedAccelerators) { | |
| 240 std::set<AcceleratorData, Cmp> achived_accelerators; | |
| 241 for (size_t i = 0; i < kAcceleratorDataWhiteListLength; ++i) | |
| 242 achived_accelerators.insert(kAcceleratorData_WhiteList20170405[i]); | |
| 243 | |
| 244 for (size_t i = 0; i < kAcceleratorDataLength; ++i) { | |
| 245 const AcceleratorData& entry = kAcceleratorData[i]; | |
| 246 if (achived_accelerators.find(entry) != achived_accelerators.end()) | |
| 247 continue; | |
| 248 EXPECT_TRUE(entry.modifiers & ui::EF_COMMAND_DOWN) | |
| 249 << "Non-Search-based accelerator:" | |
| 250 << " keycode=" << entry.keycode | |
| 251 << " SHIFT=" << (entry.modifiers & ui::EF_SHIFT_DOWN) | |
| 252 << " CONTROL=" << (entry.modifiers & ui::EF_CONTROL_DOWN) | |
| 253 << " ALT=" << (entry.modifiers & ui::EF_ALT_DOWN); | |
| 254 } | |
| 255 } | |
| 256 | |
| 104 } // namespace ash | 257 } // namespace ash |
| OLD | NEW |