Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(272)

Side by Side Diff: components/exo/shell_surface.cc

Issue 2706543002: exo: Synchronize multi-display window positioning (Closed)
Patch Set: Fix shadow and address comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/exo/shell_surface.h ('k') | components/exo/shell_surface_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 "components/exo/shell_surface.h" 5 #include "components/exo/shell_surface.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "ash/common/frame/custom_frame_view_ash.h" 9 #include "ash/common/frame/custom_frame_view_ash.h"
10 #include "ash/common/shelf/wm_shelf.h" 10 #include "ash/common/shelf/wm_shelf.h"
(...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 // Commit() is called. 362 // Commit() is called.
363 pending_origin_offset_ += config.origin_offset; 363 pending_origin_offset_ += config.origin_offset;
364 364
365 // Set the resize direction that will be applied when Commit() is called. 365 // Set the resize direction that will be applied when Commit() is called.
366 pending_resize_component_ = config.resize_component; 366 pending_resize_component_ = config.resize_component;
367 367
368 if (config.serial == serial) 368 if (config.serial == serial)
369 break; 369 break;
370 } 370 }
371 371
372 if (widget_) 372 if (widget_) {
373 UpdateWidgetBounds(); 373 UpdateWidgetBounds();
374 UpdateShadow();
375 }
374 } 376 }
375 377
376 void ShellSurface::SetParent(ShellSurface* parent) { 378 void ShellSurface::SetParent(ShellSurface* parent) {
377 TRACE_EVENT1("exo", "ShellSurface::SetParent", "parent", 379 TRACE_EVENT1("exo", "ShellSurface::SetParent", "parent",
378 parent ? base::UTF16ToASCII(parent->title_) : "null"); 380 parent ? base::UTF16ToASCII(parent->title_) : "null");
379 381
380 if (parent_) { 382 if (parent_) {
381 parent_->RemoveObserver(this); 383 parent_->RemoveObserver(this);
382 if (widget_) 384 if (widget_)
383 wm::RemoveTransientChild(parent_, widget_->GetNativeWindow()); 385 wm::RemoveTransientChild(parent_, widget_->GetNativeWindow());
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
622 624
623 void ShellSurface::SetTopInset(int height) { 625 void ShellSurface::SetTopInset(int height) {
624 TRACE_EVENT1("exo", "ShellSurface::SetTopInset", "height", height); 626 TRACE_EVENT1("exo", "ShellSurface::SetTopInset", "height", height);
625 627
626 pending_top_inset_height_ = height; 628 pending_top_inset_height_ = height;
627 } 629 }
628 630
629 void ShellSurface::SetOrigin(const gfx::Point& origin) { 631 void ShellSurface::SetOrigin(const gfx::Point& origin) {
630 TRACE_EVENT1("exo", "ShellSurface::SetOrigin", "origin", origin.ToString()); 632 TRACE_EVENT1("exo", "ShellSurface::SetOrigin", "origin", origin.ToString());
631 633
634 if (origin == origin_)
635 return;
636
637 if (bounds_mode_ != BoundsMode::CLIENT) {
638 origin_ = origin;
639 return;
640 }
641
642 // If the origin changed, give the client a chance to adjust window positions
643 // before switching to the new coordinate system. Retain the old origin by
644 // reverting the origin delta until the next configure is acknowledged.
645 gfx::Vector2d delta = origin - origin_;
646 origin_offset_ -= delta;
647 pending_origin_offset_accumulator_ += delta;
648
632 origin_ = origin; 649 origin_ = origin;
650
651 if (widget_) {
652 UpdateWidgetBounds();
653 UpdateShadow();
654 }
655
656 Configure();
633 } 657 }
634 658
635 void ShellSurface::SetActivatable(bool activatable) { 659 void ShellSurface::SetActivatable(bool activatable) {
636 TRACE_EVENT1("exo", "ShellSurface::SetActivatable", "activatable", 660 TRACE_EVENT1("exo", "ShellSurface::SetActivatable", "activatable",
637 activatable); 661 activatable);
638 662
639 activatable_ = activatable; 663 activatable_ = activatable;
640 } 664 }
641 665
642 void ShellSurface::SetContainer(int container) { 666 void ShellSurface::SetContainer(int container) {
(...skipping 26 matching lines...) Expand all
669 693
670 //////////////////////////////////////////////////////////////////////////////// 694 ////////////////////////////////////////////////////////////////////////////////
671 // SurfaceDelegate overrides: 695 // SurfaceDelegate overrides:
672 696
673 void ShellSurface::OnSurfaceCommit() { 697 void ShellSurface::OnSurfaceCommit() {
674 surface_->CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces(); 698 surface_->CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
675 surface_->CommitSurfaceHierarchy(); 699 surface_->CommitSurfaceHierarchy();
676 700
677 if (enabled() && !widget_) { 701 if (enabled() && !widget_) {
678 // Defer widget creation until surface contains some contents. 702 // Defer widget creation until surface contains some contents.
679 if (surface_->content_size().IsEmpty()) 703 if (surface_->content_size().IsEmpty()) {
680 Configure(); 704 Configure();
681 else 705 return;
682 CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL); 706 }
707
708 CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
683 } 709 }
684 710
685 // Apply the accumulated pending origin offset to reflect acknowledged 711 // Apply the accumulated pending origin offset to reflect acknowledged
686 // configure requests. 712 // configure requests.
687 origin_offset_ += pending_origin_offset_; 713 origin_offset_ += pending_origin_offset_;
688 pending_origin_offset_ = gfx::Vector2d(); 714 pending_origin_offset_ = gfx::Vector2d();
689 715
690 // Update resize direction to reflect acknowledged configure requests. 716 // Update resize direction to reflect acknowledged configure requests.
691 resize_component_ = pending_resize_component_; 717 resize_component_ = pending_resize_component_;
692 718
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
864 ash::wm::WindowState* window_state, 890 ash::wm::WindowState* window_state,
865 ash::wm::WindowStateType old_type) { 891 ash::wm::WindowStateType old_type) {
866 ash::wm::WindowStateType new_type = window_state->GetStateType(); 892 ash::wm::WindowStateType new_type = window_state->GetStateType();
867 if (ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(old_type) || 893 if (ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(old_type) ||
868 ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) { 894 ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) {
869 // When transitioning in/out of maximized or fullscreen mode we need to 895 // When transitioning in/out of maximized or fullscreen mode we need to
870 // make sure we have a configure callback before we allow the default 896 // make sure we have a configure callback before we allow the default
871 // cross-fade animations. The configure callback provides a mechanism for 897 // cross-fade animations. The configure callback provides a mechanism for
872 // the client to inform us that a frame has taken the state change into 898 // the client to inform us that a frame has taken the state change into
873 // account and without this cross-fade animations are unreliable. 899 // account and without this cross-fade animations are unreliable.
874 if (configure_callback_.is_null()) 900
901 // TODO(domlaskowski): For shell surfaces whose bounds are controlled by the
902 // client, the configure callback does not yet support window state changes.
903 if (configure_callback_.is_null() || bounds_mode_ == BoundsMode::CLIENT)
875 scoped_animations_disabled_.reset(new ScopedAnimationsDisabled(this)); 904 scoped_animations_disabled_.reset(new ScopedAnimationsDisabled(this));
876 } 905 }
877 } 906 }
878 907
879 void ShellSurface::OnPostWindowStateTypeChange( 908 void ShellSurface::OnPostWindowStateTypeChange(
880 ash::wm::WindowState* window_state, 909 ash::wm::WindowState* window_state,
881 ash::wm::WindowStateType old_type) { 910 ash::wm::WindowStateType old_type) {
882 ash::wm::WindowStateType new_type = window_state->GetStateType(); 911 ash::wm::WindowStateType new_type = window_state->GetStateType();
883 if (ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(old_type) || 912 if (ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(old_type) ||
884 ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) { 913 ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) {
(...skipping 11 matching lines...) Expand all
896 // Re-enable animations if they were disabled in pre state change handler. 925 // Re-enable animations if they were disabled in pre state change handler.
897 scoped_animations_disabled_.reset(); 926 scoped_animations_disabled_.reset();
898 } 927 }
899 928
900 //////////////////////////////////////////////////////////////////////////////// 929 ////////////////////////////////////////////////////////////////////////////////
901 // aura::WindowObserver overrides: 930 // aura::WindowObserver overrides:
902 931
903 void ShellSurface::OnWindowBoundsChanged(aura::Window* window, 932 void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
904 const gfx::Rect& old_bounds, 933 const gfx::Rect& old_bounds,
905 const gfx::Rect& new_bounds) { 934 const gfx::Rect& new_bounds) {
935 // TODO(domlaskowski): For shell surfaces whose bounds are controlled by the
936 // client, the configure callback does not yet support resizing.
937 if (bounds_mode_ == BoundsMode::CLIENT)
938 return;
939
906 if (!widget_ || !surface_ || ignore_window_bounds_changes_) 940 if (!widget_ || !surface_ || ignore_window_bounds_changes_)
907 return; 941 return;
908 942
909 if (window == widget_->GetNativeWindow()) { 943 if (window == widget_->GetNativeWindow()) {
910 if (new_bounds.size() == old_bounds.size()) 944 if (new_bounds.size() == old_bounds.size())
911 return; 945 return;
912 946
913 // If size changed then give the client a chance to produce new contents 947 // If size changed then give the client a chance to produce new contents
914 // before origin on screen is changed. Retain the old origin by reverting 948 // before origin on screen is changed. Retain the old origin by reverting
915 // the origin delta until the next configure is acknowledged. 949 // the origin delta until the next configure is acknowledged.
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after
1147 resize_component = window_state->drag_details()->window_component; 1181 resize_component = window_state->drag_details()->window_component;
1148 } 1182 }
1149 1183
1150 uint32_t serial = 0; 1184 uint32_t serial = 0;
1151 if (!configure_callback_.is_null()) { 1185 if (!configure_callback_.is_null()) {
1152 if (widget_) { 1186 if (widget_) {
1153 const views::NonClientView* non_client_view = widget_->non_client_view(); 1187 const views::NonClientView* non_client_view = widget_->non_client_view();
1154 serial = configure_callback_.Run( 1188 serial = configure_callback_.Run(
1155 non_client_view->frame_view()->GetBoundsForClientView().size(), 1189 non_client_view->frame_view()->GetBoundsForClientView().size(),
1156 ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(), 1190 ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(),
1157 IsResizing(), widget_->IsActive()); 1191 IsResizing(), widget_->IsActive(), origin_);
1158 } else { 1192 } else {
1159 serial = configure_callback_.Run( 1193 serial = configure_callback_.Run(gfx::Size(),
1160 gfx::Size(), ash::wm::WINDOW_STATE_TYPE_NORMAL, false, false); 1194 ash::wm::WINDOW_STATE_TYPE_NORMAL, false,
1195 false, origin_);
1161 } 1196 }
1162 } 1197 }
1163 1198
1164 if (!serial) { 1199 if (!serial) {
1165 pending_origin_offset_ += origin_offset; 1200 pending_origin_offset_ += origin_offset;
1166 pending_resize_component_ = resize_component; 1201 pending_resize_component_ = resize_component;
1167 return; 1202 return;
1168 } 1203 }
1169 1204
1170 // Apply origin offset and resize component at the first Commit() after this 1205 // Apply origin offset and resize component at the first Commit() after this
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
1283 ash::WindowResizer::kBoundsChange_Resizes; 1318 ash::WindowResizer::kBoundsChange_Resizes;
1284 } 1319 }
1285 1320
1286 gfx::Rect ShellSurface::GetVisibleBounds() const { 1321 gfx::Rect ShellSurface::GetVisibleBounds() const {
1287 // Use |geometry_| if set, otherwise use the visual bounds of the surface. 1322 // Use |geometry_| if set, otherwise use the visual bounds of the surface.
1288 return geometry_.IsEmpty() ? gfx::Rect(surface_->window()->layer()->size()) 1323 return geometry_.IsEmpty() ? gfx::Rect(surface_->window()->layer()->size())
1289 : geometry_; 1324 : geometry_;
1290 } 1325 }
1291 1326
1292 gfx::Point ShellSurface::GetSurfaceOrigin() const { 1327 gfx::Point ShellSurface::GetSurfaceOrigin() const {
1293 // For client-positioned shell surfaces, the surface origin corresponds to the 1328 DCHECK(bounds_mode_ == BoundsMode::SHELL || resize_component_ == HTCAPTION);
1294 // widget position relative to the origin specified by the client. Since the
1295 // surface is positioned relative to the widget, negate this vector to align
1296 // the surface with the widget.
1297 if (bounds_mode_ != BoundsMode::SHELL) {
1298 gfx::Point position = widget_->GetNativeWindow()->bounds().origin();
1299 wm::ConvertPointToScreen(widget_->GetNativeWindow()->parent(), &position);
1300 return origin_ - position.OffsetFromOrigin();
1301 }
1302 1329
1303 gfx::Rect visible_bounds = GetVisibleBounds(); 1330 gfx::Rect visible_bounds = GetVisibleBounds();
1304 gfx::Rect client_bounds = 1331 gfx::Rect client_bounds =
1305 widget_->non_client_view()->frame_view()->GetBoundsForClientView(); 1332 widget_->non_client_view()->frame_view()->GetBoundsForClientView();
1306 switch (resize_component_) { 1333 switch (resize_component_) {
1307 case HTCAPTION: 1334 case HTCAPTION:
1308 return gfx::Point() + origin_offset_ - visible_bounds.OffsetFromOrigin(); 1335 return (bounds_mode_ == BoundsMode::CLIENT ? origin_ : gfx::Point()) +
1336 origin_offset_ - visible_bounds.OffsetFromOrigin();
reveman 2017/02/24 16:50:00 nit: split into multiple lines to make it easier t
Dominik Laskowski 2017/02/24 18:51:30 Done.
1309 case HTBOTTOM: 1337 case HTBOTTOM:
1310 case HTRIGHT: 1338 case HTRIGHT:
1311 case HTBOTTOMRIGHT: 1339 case HTBOTTOMRIGHT:
1312 return gfx::Point() - visible_bounds.OffsetFromOrigin(); 1340 return gfx::Point() - visible_bounds.OffsetFromOrigin();
1313 case HTTOP: 1341 case HTTOP:
1314 case HTTOPRIGHT: 1342 case HTTOPRIGHT:
1315 return gfx::Point(0, client_bounds.height() - visible_bounds.height()) - 1343 return gfx::Point(0, client_bounds.height() - visible_bounds.height()) -
1316 visible_bounds.OffsetFromOrigin(); 1344 visible_bounds.OffsetFromOrigin();
1317 case HTLEFT: 1345 case HTLEFT:
1318 case HTBOTTOMLEFT: 1346 case HTBOTTOMLEFT:
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1350 return; 1378 return;
1351 1379
1352 gfx::Rect visible_bounds = GetVisibleBounds(); 1380 gfx::Rect visible_bounds = GetVisibleBounds();
1353 gfx::Rect new_widget_bounds = 1381 gfx::Rect new_widget_bounds =
1354 widget_->non_client_view()->GetWindowBoundsForClientBounds( 1382 widget_->non_client_view()->GetWindowBoundsForClientBounds(
1355 visible_bounds); 1383 visible_bounds);
1356 1384
1357 switch (bounds_mode_) { 1385 switch (bounds_mode_) {
1358 case BoundsMode::CLIENT: 1386 case BoundsMode::CLIENT:
1359 case BoundsMode::FIXED: 1387 case BoundsMode::FIXED:
1360 // Position is relative to the origin. 1388 new_widget_bounds.set_origin(origin_ -
1361 new_widget_bounds += origin_.OffsetFromOrigin(); 1389 GetSurfaceOrigin().OffsetFromOrigin());
1362 break; 1390 break;
1363 case BoundsMode::SHELL: 1391 case BoundsMode::SHELL:
1364 // Update widget origin using the surface origin if the current location 1392 // Update widget origin using the surface origin if the current location
1365 // of surface is being anchored to one side of the widget as a result of a 1393 // of surface is being anchored to one side of the widget as a result of a
1366 // resize operation. 1394 // resize operation.
1367 if (resize_component_ != HTCAPTION) { 1395 if (resize_component_ != HTCAPTION) {
1368 gfx::Point widget_origin = 1396 gfx::Point widget_origin =
1369 GetSurfaceOrigin() + visible_bounds.OffsetFromOrigin(); 1397 GetSurfaceOrigin() + visible_bounds.OffsetFromOrigin();
1370 wm::ConvertPointToScreen(widget_->GetNativeWindow(), &widget_origin); 1398 wm::ConvertPointToScreen(widget_->GetNativeWindow(), &widget_origin);
1371 new_widget_bounds.set_origin(widget_origin); 1399 new_widget_bounds.set_origin(widget_origin);
(...skipping 28 matching lines...) Expand all
1400 return; 1428 return;
1401 aura::Window* window = widget_->GetNativeWindow(); 1429 aura::Window* window = widget_->GetNativeWindow();
1402 if (!shadow_enabled_) { 1430 if (!shadow_enabled_) {
1403 wm::SetShadowElevation(window, wm::ShadowElevation::NONE); 1431 wm::SetShadowElevation(window, wm::ShadowElevation::NONE);
1404 if (shadow_underlay_) 1432 if (shadow_underlay_)
1405 shadow_underlay_->Hide(); 1433 shadow_underlay_->Hide();
1406 } else { 1434 } else {
1407 wm::SetShadowElevation(window, wm::ShadowElevation::MEDIUM); 1435 wm::SetShadowElevation(window, wm::ShadowElevation::MEDIUM);
1408 gfx::Rect shadow_content_bounds = 1436 gfx::Rect shadow_content_bounds =
1409 gfx::ScaleToEnclosedRect(shadow_content_bounds_, 1.f / scale_); 1437 gfx::ScaleToEnclosedRect(shadow_content_bounds_, 1.f / scale_);
1438
1439 // Convert from screen to display coordinates.
1440 if (!shadow_content_bounds.IsEmpty()) {
1441 gfx::Point origin = shadow_content_bounds.origin() - origin_offset_;
1442 wm::ConvertPointFromScreen(window->parent(), &origin);
1443 shadow_content_bounds.set_origin(origin);
1444 }
1445
1410 gfx::Rect shadow_underlay_bounds = shadow_content_bounds_; 1446 gfx::Rect shadow_underlay_bounds = shadow_content_bounds_;
1411 if (shadow_underlay_bounds.IsEmpty()) 1447
1448 if (shadow_underlay_bounds.IsEmpty()) {
1412 shadow_underlay_bounds = gfx::Rect(surface_->window()->bounds().size()); 1449 shadow_underlay_bounds = gfx::Rect(surface_->window()->bounds().size());
1450 } else if (shadow_underlay_in_surface_) {
1451 // Since the shadow underlay is positioned relative to the surface, its
1452 // origin corresponds to the shadow content position relative to the
1453 // origin specified by the client.
1454 shadow_underlay_bounds -=
1455 gfx::ScaleToCeiledPoint(origin_ + origin_offset_, scale_)
1456 .OffsetFromOrigin();
1457 }
1413 1458
1414 if (!shadow_underlay_in_surface_) { 1459 if (!shadow_underlay_in_surface_) {
1415 shadow_content_bounds = shadow_content_bounds_; 1460 shadow_content_bounds = shadow_content_bounds_;
1416 if (shadow_content_bounds.IsEmpty()) { 1461 if (shadow_content_bounds.IsEmpty()) {
1417 shadow_content_bounds = window->bounds(); 1462 shadow_content_bounds = window->bounds();
1463 } else {
1464 // Convert from screen to display coordinates.
1465 gfx::Point origin = shadow_content_bounds.origin() - origin_offset_;
1466 wm::ConvertPointFromScreen(window->parent(), &origin);
1467 shadow_content_bounds.set_origin(origin);
1418 } 1468 }
1419 } 1469 }
1420 1470
1421 // TODO(oshima): Adjust the coordinates from client screen to
1422 // chromeos screen when multi displays are supported.
1423 gfx::Point origin = window->bounds().origin();
1424 gfx::Point shadow_origin = shadow_content_bounds.origin(); 1471 gfx::Point shadow_origin = shadow_content_bounds.origin();
1425 shadow_origin -= origin.OffsetFromOrigin(); 1472 shadow_origin -= window->bounds().OffsetFromOrigin();
1426 gfx::Rect shadow_bounds(shadow_origin, shadow_content_bounds.size()); 1473 gfx::Rect shadow_bounds(shadow_origin, shadow_content_bounds.size());
1427 1474
1428 // Always create and show the underlay, even in maximized/fullscreen. 1475 // Always create and show the underlay, even in maximized/fullscreen.
1429 if (!shadow_underlay_) { 1476 if (!shadow_underlay_) {
1430 shadow_underlay_ = new aura::Window(nullptr); 1477 shadow_underlay_ = new aura::Window(nullptr);
1431 shadow_underlay_event_handler_ = 1478 shadow_underlay_event_handler_ =
1432 base::MakeUnique<ShadowUnderlayEventHandler>(); 1479 base::MakeUnique<ShadowUnderlayEventHandler>();
1433 shadow_underlay_->SetTargetHandler(shadow_underlay_event_handler_.get()); 1480 shadow_underlay_->SetTargetHandler(shadow_underlay_event_handler_.get());
1434 DCHECK(shadow_underlay_->owned_by_parent()); 1481 DCHECK(shadow_underlay_->owned_by_parent());
1435 // Ensure the background area inside the shadow is solid black. 1482 // Ensure the background area inside the shadow is solid black.
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
1521 // small style shadow for them. 1568 // small style shadow for them.
1522 if (!activatable_) 1569 if (!activatable_)
1523 shadow->SetElevation(wm::ShadowElevation::SMALL); 1570 shadow->SetElevation(wm::ShadowElevation::SMALL);
1524 // We don't have rounded corners unless frame is enabled. 1571 // We don't have rounded corners unless frame is enabled.
1525 if (!frame_enabled_) 1572 if (!frame_enabled_)
1526 shadow->SetRoundedCornerRadius(0); 1573 shadow->SetRoundedCornerRadius(0);
1527 } 1574 }
1528 } 1575 }
1529 1576
1530 } // namespace exo 1577 } // namespace exo
OLDNEW
« no previous file with comments | « components/exo/shell_surface.h ('k') | components/exo/shell_surface_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698