| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "ash/display/display_util.h" | 5 #include "ash/display/display_util.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "ash/common/new_window_delegate.h" | 9 #include "ash/common/new_window_delegate.h" |
| 10 #include "ash/common/system/system_notifier.h" | 10 #include "ash/common/system/system_notifier.h" |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 WmShell::Get()->new_window_delegate()->OpenFeedbackPage(); | 52 WmShell::Get()->new_window_delegate()->OpenFeedbackPage(); |
| 53 } | 53 } |
| 54 | 54 |
| 55 private: | 55 private: |
| 56 // Private destructor since NotificationDelegate is ref-counted. | 56 // Private destructor since NotificationDelegate is ref-counted. |
| 57 ~DisplayErrorNotificationDelegate() override = default; | 57 ~DisplayErrorNotificationDelegate() override = default; |
| 58 | 58 |
| 59 DISALLOW_COPY_AND_ASSIGN(DisplayErrorNotificationDelegate); | 59 DISALLOW_COPY_AND_ASSIGN(DisplayErrorNotificationDelegate); |
| 60 }; | 60 }; |
| 61 | 61 |
| 62 // List of value UI Scale values. Scales for 2x are equivalent to 640, | |
| 63 // 800, 1024, 1280, 1440, 1600 and 1920 pixel width respectively on | |
| 64 // 2560 pixel width 2x density display. Please see crbug.com/233375 | |
| 65 // for the full list of resolutions. | |
| 66 const float kUIScalesFor2x[] = {0.5f, 0.625f, 0.8f, 1.0f, | |
| 67 1.125f, 1.25f, 1.5f, 2.0f}; | |
| 68 const float kUIScalesFor1_25x[] = {0.5f, 0.625f, 0.8f, 1.0f, 1.25f}; | |
| 69 const float kUIScalesFor1280[] = {0.5f, 0.625f, 0.8f, 1.0f, 1.125f}; | |
| 70 const float kUIScalesFor1366[] = {0.5f, 0.6f, 0.75f, 1.0f, 1.125f}; | |
| 71 | |
| 72 std::vector<float> GetScalesForDisplay( | |
| 73 const scoped_refptr<ui::ManagedDisplayMode>& native_mode) { | |
| 74 #define ASSIGN_ARRAY(v, a) v.assign(a, a + arraysize(a)) | |
| 75 | |
| 76 std::vector<float> ret; | |
| 77 if (native_mode->device_scale_factor() == 2.0f) { | |
| 78 ASSIGN_ARRAY(ret, kUIScalesFor2x); | |
| 79 return ret; | |
| 80 } else if (native_mode->device_scale_factor() == 1.25f) { | |
| 81 ASSIGN_ARRAY(ret, kUIScalesFor1_25x); | |
| 82 return ret; | |
| 83 } | |
| 84 switch (native_mode->size().width()) { | |
| 85 case 1280: | |
| 86 ASSIGN_ARRAY(ret, kUIScalesFor1280); | |
| 87 break; | |
| 88 case 1366: | |
| 89 ASSIGN_ARRAY(ret, kUIScalesFor1366); | |
| 90 break; | |
| 91 default: | |
| 92 ASSIGN_ARRAY(ret, kUIScalesFor1280); | |
| 93 #if defined(OS_CHROMEOS) | |
| 94 if (base::SysInfo::IsRunningOnChromeOS()) | |
| 95 NOTREACHED() << "Unknown resolution:" << native_mode->size().ToString(); | |
| 96 #endif | |
| 97 } | |
| 98 return ret; | |
| 99 } | |
| 100 | |
| 101 struct ScaleComparator { | |
| 102 explicit ScaleComparator(float s) : scale(s) {} | |
| 103 | |
| 104 bool operator()(const scoped_refptr<ui::ManagedDisplayMode>& mode) const { | |
| 105 const float kEpsilon = 0.0001f; | |
| 106 return std::abs(scale - mode->ui_scale()) < kEpsilon; | |
| 107 } | |
| 108 float scale; | |
| 109 }; | |
| 110 | 62 |
| 111 void ConvertPointFromScreenToNative(aura::WindowTreeHost* host, | 63 void ConvertPointFromScreenToNative(aura::WindowTreeHost* host, |
| 112 gfx::Point* point) { | 64 gfx::Point* point) { |
| 113 ::wm::ConvertPointFromScreen(host->window(), point); | 65 ::wm::ConvertPointFromScreen(host->window(), point); |
| 114 host->ConvertPointToNativeScreen(point); | 66 host->ConvertPointToNativeScreen(point); |
| 115 } | 67 } |
| 116 | 68 |
| 117 scoped_refptr<ui::ManagedDisplayMode> GetDisplayModeForUIScale( | 69 scoped_refptr<ui::ManagedDisplayMode> GetDisplayModeForUIScale( |
| 118 const ui::ManagedDisplayInfo& info, | 70 const ui::ManagedDisplayInfo& info, |
| 119 float ui_scale) { | 71 float ui_scale) { |
| 120 const ui::ManagedDisplayInfo::ManagedDisplayModeList& modes = | 72 const ui::ManagedDisplayInfo::ManagedDisplayModeList& modes = |
| 121 info.display_modes(); | 73 info.display_modes(); |
| 122 auto iter = std::find_if( | 74 auto iter = std::find_if( |
| 123 modes.begin(), modes.end(), | 75 modes.begin(), modes.end(), |
| 124 [ui_scale](const scoped_refptr<ui::ManagedDisplayMode>& mode) { | 76 [ui_scale](const scoped_refptr<ui::ManagedDisplayMode>& mode) { |
| 125 return mode->ui_scale() == ui_scale; | 77 return mode->ui_scale() == ui_scale; |
| 126 }); | 78 }); |
| 127 if (iter == modes.end()) | 79 if (iter == modes.end()) |
| 128 return scoped_refptr<ui::ManagedDisplayMode>(); | 80 return scoped_refptr<ui::ManagedDisplayMode>(); |
| 129 return *iter; | 81 return *iter; |
| 130 } | 82 } |
| 131 | 83 |
| 132 scoped_refptr<ui::ManagedDisplayMode> FindNextMode( | |
| 133 const ui::ManagedDisplayInfo::ManagedDisplayModeList& modes, | |
| 134 size_t index, | |
| 135 bool up) { | |
| 136 DCHECK_LT(index, modes.size()); | |
| 137 size_t new_index = index; | |
| 138 if (up && (index + 1 < modes.size())) | |
| 139 ++new_index; | |
| 140 else if (!up && index != 0) | |
| 141 --new_index; | |
| 142 return modes[new_index]; | |
| 143 } | |
| 144 | 84 |
| 145 } // namespace | 85 } // namespace |
| 146 | 86 |
| 147 ui::ManagedDisplayInfo::ManagedDisplayModeList | |
| 148 CreateInternalManagedDisplayModeList( | |
| 149 const scoped_refptr<ui::ManagedDisplayMode>& native_mode) { | |
| 150 ui::ManagedDisplayInfo::ManagedDisplayModeList display_mode_list; | |
| 151 | |
| 152 float native_ui_scale = (native_mode->device_scale_factor() == 1.25f) | |
| 153 ? 1.0f | |
| 154 : native_mode->device_scale_factor(); | |
| 155 for (float ui_scale : GetScalesForDisplay(native_mode)) { | |
| 156 scoped_refptr<ui::ManagedDisplayMode> mode(new ui::ManagedDisplayMode( | |
| 157 native_mode->size(), native_mode->refresh_rate(), | |
| 158 native_mode->is_interlaced(), ui_scale == native_ui_scale, ui_scale, | |
| 159 native_mode->device_scale_factor())); | |
| 160 display_mode_list.push_back(mode); | |
| 161 } | |
| 162 return display_mode_list; | |
| 163 } | |
| 164 | |
| 165 ui::ManagedDisplayInfo::ManagedDisplayModeList | |
| 166 CreateUnifiedManagedDisplayModeList( | |
| 167 const scoped_refptr<ui::ManagedDisplayMode>& native_mode, | |
| 168 const std::set<std::pair<float, float>>& dsf_scale_list) { | |
| 169 ui::ManagedDisplayInfo::ManagedDisplayModeList display_mode_list; | |
| 170 | |
| 171 for (auto& pair : dsf_scale_list) { | |
| 172 gfx::SizeF scaled_size(native_mode->size()); | |
| 173 scaled_size.Scale(pair.second); | |
| 174 scoped_refptr<ui::ManagedDisplayMode> mode(new ui::ManagedDisplayMode( | |
| 175 gfx::ToFlooredSize(scaled_size), native_mode->refresh_rate(), | |
| 176 native_mode->is_interlaced(), false /* native */, | |
| 177 native_mode->ui_scale(), pair.first /* device_scale_factor */)); | |
| 178 display_mode_list.push_back(mode); | |
| 179 } | |
| 180 // Sort the mode by the size in DIP. | |
| 181 std::sort(display_mode_list.begin(), display_mode_list.end(), | |
| 182 [](const scoped_refptr<ui::ManagedDisplayMode>& a, | |
| 183 const scoped_refptr<ui::ManagedDisplayMode>& b) { | |
| 184 return a->GetSizeInDIP(false).GetArea() < | |
| 185 b->GetSizeInDIP(false).GetArea(); | |
| 186 }); | |
| 187 return display_mode_list; | |
| 188 } | |
| 189 | |
| 190 scoped_refptr<ui::ManagedDisplayMode> GetDisplayModeForResolution( | |
| 191 const ui::ManagedDisplayInfo& info, | |
| 192 const gfx::Size& resolution) { | |
| 193 if (display::Display::IsInternalDisplayId(info.id())) | |
| 194 return scoped_refptr<ui::ManagedDisplayMode>(); | |
| 195 | |
| 196 const ui::ManagedDisplayInfo::ManagedDisplayModeList& modes = | |
| 197 info.display_modes(); | |
| 198 DCHECK_NE(0u, modes.size()); | |
| 199 scoped_refptr<ui::ManagedDisplayMode> target_mode; | |
| 200 ui::ManagedDisplayInfo::ManagedDisplayModeList::const_iterator iter = | |
| 201 std::find_if( | |
| 202 modes.begin(), modes.end(), | |
| 203 [resolution](const scoped_refptr<ui::ManagedDisplayMode>& mode) { | |
| 204 return mode->size() == resolution; | |
| 205 }); | |
| 206 if (iter == modes.end()) { | |
| 207 LOG(WARNING) << "Unsupported resolution was requested:" | |
| 208 << resolution.ToString(); | |
| 209 return scoped_refptr<ui::ManagedDisplayMode>(); | |
| 210 } | |
| 211 return *iter; | |
| 212 } | |
| 213 | |
| 214 scoped_refptr<ui::ManagedDisplayMode> GetDisplayModeForNextUIScale( | |
| 215 const ui::ManagedDisplayInfo& info, | |
| 216 bool up) { | |
| 217 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); | |
| 218 if (!display_manager->IsActiveDisplayId(info.id()) || | |
| 219 !display::Display::IsInternalDisplayId(info.id())) { | |
| 220 return scoped_refptr<ui::ManagedDisplayMode>(); | |
| 221 } | |
| 222 const ui::ManagedDisplayInfo::ManagedDisplayModeList& modes = | |
| 223 info.display_modes(); | |
| 224 ScaleComparator comparator(info.configured_ui_scale()); | |
| 225 auto iter = std::find_if(modes.begin(), modes.end(), comparator); | |
| 226 return FindNextMode(modes, iter - modes.begin(), up); | |
| 227 } | |
| 228 | |
| 229 scoped_refptr<ui::ManagedDisplayMode> GetDisplayModeForNextResolution( | |
| 230 const ui::ManagedDisplayInfo& info, | |
| 231 bool up) { | |
| 232 if (display::Display::IsInternalDisplayId(info.id())) | |
| 233 return scoped_refptr<ui::ManagedDisplayMode>(); | |
| 234 | |
| 235 const ui::ManagedDisplayInfo::ManagedDisplayModeList& modes = | |
| 236 info.display_modes(); | |
| 237 scoped_refptr<ui::ManagedDisplayMode> tmp = new ui::ManagedDisplayMode( | |
| 238 info.size_in_pixel(), 0.0, false, false, 1.0, info.device_scale_factor()); | |
| 239 gfx::Size resolution = tmp->GetSizeInDIP(false); | |
| 240 | |
| 241 auto iter = std::find_if( | |
| 242 modes.begin(), modes.end(), | |
| 243 [resolution](const scoped_refptr<ui::ManagedDisplayMode>& mode) { | |
| 244 return mode->GetSizeInDIP(false) == resolution; | |
| 245 }); | |
| 246 return FindNextMode(modes, iter - modes.begin(), up); | |
| 247 } | |
| 248 | |
| 249 bool SetDisplayUIScale(int64_t id, float ui_scale) { | 87 bool SetDisplayUIScale(int64_t id, float ui_scale) { |
| 250 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); | 88 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); |
| 251 if (!display_manager->IsActiveDisplayId(id) || | 89 if (!display_manager->IsActiveDisplayId(id) || |
| 252 !display::Display::IsInternalDisplayId(id)) { | 90 !display::Display::IsInternalDisplayId(id)) { |
| 253 return false; | 91 return false; |
| 254 } | 92 } |
| 255 const ui::ManagedDisplayInfo& info = display_manager->GetDisplayInfo(id); | 93 const ui::ManagedDisplayInfo& info = display_manager->GetDisplayInfo(id); |
| 256 | 94 |
| 257 scoped_refptr<ui::ManagedDisplayMode> mode = | 95 scoped_refptr<ui::ManagedDisplayMode> mode = |
| 258 GetDisplayModeForUIScale(info, ui_scale); | 96 GetDisplayModeForUIScale(info, ui_scale); |
| 259 if (!mode) | 97 if (!mode) |
| 260 return false; | 98 return false; |
| 261 return display_manager->SetDisplayMode(id, mode); | 99 return display_manager->SetDisplayMode(id, mode); |
| 262 } | 100 } |
| 263 | 101 |
| 264 bool HasDisplayModeForUIScale(const ui::ManagedDisplayInfo& info, | |
| 265 float ui_scale) { | |
| 266 ScaleComparator comparator(ui_scale); | |
| 267 const ui::ManagedDisplayInfo::ManagedDisplayModeList& modes = | |
| 268 info.display_modes(); | |
| 269 return std::find_if(modes.begin(), modes.end(), comparator) != modes.end(); | |
| 270 } | |
| 271 | |
| 272 bool ComputeBoundary(const display::Display& a_display, | |
| 273 const display::Display& b_display, | |
| 274 gfx::Rect* a_edge_in_screen, | |
| 275 gfx::Rect* b_edge_in_screen) { | |
| 276 const gfx::Rect& a_bounds = a_display.bounds(); | |
| 277 const gfx::Rect& b_bounds = b_display.bounds(); | |
| 278 | |
| 279 // Find touching side. | |
| 280 int rx = std::max(a_bounds.x(), b_bounds.x()); | |
| 281 int ry = std::max(a_bounds.y(), b_bounds.y()); | |
| 282 int rr = std::min(a_bounds.right(), b_bounds.right()); | |
| 283 int rb = std::min(a_bounds.bottom(), b_bounds.bottom()); | |
| 284 | |
| 285 display::DisplayPlacement::Position position; | |
| 286 if ((rb - ry) == 0) { | |
| 287 // top bottom | |
| 288 if (a_bounds.bottom() == b_bounds.y()) { | |
| 289 position = display::DisplayPlacement::BOTTOM; | |
| 290 } else if (a_bounds.y() == b_bounds.bottom()) { | |
| 291 position = display::DisplayPlacement::TOP; | |
| 292 } else { | |
| 293 return false; | |
| 294 } | |
| 295 } else { | |
| 296 // left right | |
| 297 if (a_bounds.right() == b_bounds.x()) { | |
| 298 position = display::DisplayPlacement::RIGHT; | |
| 299 } else if (a_bounds.x() == b_bounds.right()) { | |
| 300 position = display::DisplayPlacement::LEFT; | |
| 301 } else { | |
| 302 DCHECK_NE(rr, rx); | |
| 303 return false; | |
| 304 } | |
| 305 } | |
| 306 | |
| 307 switch (position) { | |
| 308 case display::DisplayPlacement::TOP: | |
| 309 case display::DisplayPlacement::BOTTOM: { | |
| 310 int left = std::max(a_bounds.x(), b_bounds.x()); | |
| 311 int right = std::min(a_bounds.right(), b_bounds.right()); | |
| 312 if (position == display::DisplayPlacement::TOP) { | |
| 313 a_edge_in_screen->SetRect(left, a_bounds.y(), right - left, 1); | |
| 314 b_edge_in_screen->SetRect(left, b_bounds.bottom() - 1, right - left, 1); | |
| 315 } else { | |
| 316 a_edge_in_screen->SetRect(left, a_bounds.bottom() - 1, right - left, 1); | |
| 317 b_edge_in_screen->SetRect(left, b_bounds.y(), right - left, 1); | |
| 318 } | |
| 319 break; | |
| 320 } | |
| 321 case display::DisplayPlacement::LEFT: | |
| 322 case display::DisplayPlacement::RIGHT: { | |
| 323 int top = std::max(a_bounds.y(), b_bounds.y()); | |
| 324 int bottom = std::min(a_bounds.bottom(), b_bounds.bottom()); | |
| 325 if (position == display::DisplayPlacement::LEFT) { | |
| 326 a_edge_in_screen->SetRect(a_bounds.x(), top, 1, bottom - top); | |
| 327 b_edge_in_screen->SetRect(b_bounds.right() - 1, top, 1, bottom - top); | |
| 328 } else { | |
| 329 a_edge_in_screen->SetRect(a_bounds.right() - 1, top, 1, bottom - top); | |
| 330 b_edge_in_screen->SetRect(b_bounds.x(), top, 1, bottom - top); | |
| 331 } | |
| 332 break; | |
| 333 } | |
| 334 } | |
| 335 return true; | |
| 336 } | |
| 337 | |
| 338 gfx::Rect GetNativeEdgeBounds(AshWindowTreeHost* ash_host, | 102 gfx::Rect GetNativeEdgeBounds(AshWindowTreeHost* ash_host, |
| 339 const gfx::Rect& bounds_in_screen) { | 103 const gfx::Rect& bounds_in_screen) { |
| 340 aura::WindowTreeHost* host = ash_host->AsWindowTreeHost(); | 104 aura::WindowTreeHost* host = ash_host->AsWindowTreeHost(); |
| 341 gfx::Rect native_bounds = host->GetBounds(); | 105 gfx::Rect native_bounds = host->GetBounds(); |
| 342 native_bounds.Inset(ash_host->GetHostInsets()); | 106 native_bounds.Inset(ash_host->GetHostInsets()); |
| 343 gfx::Point start_in_native = bounds_in_screen.origin(); | 107 gfx::Point start_in_native = bounds_in_screen.origin(); |
| 344 gfx::Point end_in_native = bounds_in_screen.bottom_right(); | 108 gfx::Point end_in_native = bounds_in_screen.bottom_right(); |
| 345 | 109 |
| 346 ConvertPointFromScreenToNative(host, &start_in_native); | 110 ConvertPointFromScreenToNative(host, &start_in_native); |
| 347 ConvertPointFromScreenToNative(host, &end_in_native); | 111 ConvertPointFromScreenToNative(host, &end_in_native); |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 405 &new_point_in_screen); | 169 &new_point_in_screen); |
| 406 } else { | 170 } else { |
| 407 new_point_in_screen = point_in_native; | 171 new_point_in_screen = point_in_native; |
| 408 host->ConvertPointFromNativeScreen(&new_point_in_screen); | 172 host->ConvertPointFromNativeScreen(&new_point_in_screen); |
| 409 ::wm::ConvertPointToScreen(host->window(), &new_point_in_screen); | 173 ::wm::ConvertPointToScreen(host->window(), &new_point_in_screen); |
| 410 } | 174 } |
| 411 aura::Env::GetInstance()->set_last_mouse_location(new_point_in_screen); | 175 aura::Env::GetInstance()->set_last_mouse_location(new_point_in_screen); |
| 412 } | 176 } |
| 413 } | 177 } |
| 414 | 178 |
| 415 int FindDisplayIndexContainingPoint( | |
| 416 const std::vector<display::Display>& displays, | |
| 417 const gfx::Point& point_in_screen) { | |
| 418 auto iter = std::find_if(displays.begin(), displays.end(), | |
| 419 [point_in_screen](const display::Display& display) { | |
| 420 return display.bounds().Contains(point_in_screen); | |
| 421 }); | |
| 422 return iter == displays.end() ? -1 : (iter - displays.begin()); | |
| 423 } | |
| 424 | |
| 425 display::DisplayIdList CreateDisplayIdList(const display::DisplayList& list) { | |
| 426 return GenerateDisplayIdList( | |
| 427 list.begin(), list.end(), | |
| 428 [](const display::Display& display) { return display.id(); }); | |
| 429 } | |
| 430 | |
| 431 void SortDisplayIdList(display::DisplayIdList* ids) { | |
| 432 std::sort(ids->begin(), ids->end(), | |
| 433 [](int64_t a, int64_t b) { return CompareDisplayIds(a, b); }); | |
| 434 } | |
| 435 | |
| 436 std::string DisplayIdListToString(const display::DisplayIdList& list) { | |
| 437 std::stringstream s; | |
| 438 const char* sep = ""; | |
| 439 for (int64_t id : list) { | |
| 440 s << sep << id; | |
| 441 sep = ","; | |
| 442 } | |
| 443 return s.str(); | |
| 444 } | |
| 445 | |
| 446 bool CompareDisplayIds(int64_t id1, int64_t id2) { | |
| 447 DCHECK_NE(id1, id2); | |
| 448 // Output index is stored in the first 8 bits. See GetDisplayIdFromEDID | |
| 449 // in edid_parser.cc. | |
| 450 int index_1 = id1 & 0xFF; | |
| 451 int index_2 = id2 & 0xFF; | |
| 452 DCHECK_NE(index_1, index_2) << id1 << " and " << id2; | |
| 453 return display::Display::IsInternalDisplayId(id1) || | |
| 454 (index_1 < index_2 && !display::Display::IsInternalDisplayId(id2)); | |
| 455 } | |
| 456 | 179 |
| 457 #if defined(OS_CHROMEOS) | 180 #if defined(OS_CHROMEOS) |
| 458 void ShowDisplayErrorNotification(int message_id) { | 181 void ShowDisplayErrorNotification(int message_id) { |
| 459 // Always remove the notification to make sure the notification appears | 182 // Always remove the notification to make sure the notification appears |
| 460 // as a popup in any situation. | 183 // as a popup in any situation. |
| 461 message_center::MessageCenter::Get()->RemoveNotification( | 184 message_center::MessageCenter::Get()->RemoveNotification( |
| 462 kDisplayErrorNotificationId, false /* by_user */); | 185 kDisplayErrorNotificationId, false /* by_user */); |
| 463 | 186 |
| 464 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | 187 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| 465 std::unique_ptr<message_center::Notification> notification( | 188 std::unique_ptr<message_center::Notification> notification( |
| (...skipping 17 matching lines...) Expand all Loading... |
| 483 message_center::NotificationList::Notifications notifications = | 206 message_center::NotificationList::Notifications notifications = |
| 484 message_center::MessageCenter::Get()->GetVisibleNotifications(); | 207 message_center::MessageCenter::Get()->GetVisibleNotifications(); |
| 485 for (auto* const notification : notifications) { | 208 for (auto* const notification : notifications) { |
| 486 if (notification->id() == kDisplayErrorNotificationId) | 209 if (notification->id() == kDisplayErrorNotificationId) |
| 487 return notification->message(); | 210 return notification->message(); |
| 488 } | 211 } |
| 489 return base::string16(); | 212 return base::string16(); |
| 490 } | 213 } |
| 491 | 214 |
| 492 } // namespace ash | 215 } // namespace ash |
| OLD | NEW |