@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2015 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -131,9 +131,6 @@ Rectangle {
131131 anchors.right: root.right
132132 width: units.gu(30)
133133
134− property var focusedApp: ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
135− property var focusedSurface: focusedApp && focusedApp.session ? focusedApp.session.lastSurface : null
136−
137134 Rectangle {
138135 id: controlRect
139136 anchors { left: parent.left; right: parent.right }
@@ -185,6 +182,17 @@ Rectangle {
185182 }
186183 }
187184 }
185+ Button {
186+ text: "Show Launcher"
187+ activeFocusOnPress: false
188+ onClicked: {
189+ if (shellLoader.status !== Loader.Ready)
190+ return;
191+
192+ var launcher = testCase.findChild(shellLoader.item, "launcher");
193+ launcher.state = "visible";
194+ }
195+ }
188196 ListItem.ItemSelector {
189197 anchors { left: parent.left; right: parent.right }
190198 activeFocusOnPress: false
@@ -274,21 +282,21 @@ Rectangle {
274282 id: fullscreeAppCheck
275283
276284 onTriggered: {
277− if (!controls.focusedSurface) return;
278− if (controls.focusedSurface.state == Mir.FullscreenState) {
279− controls.focusedSurface.state = Mir.RestoredState;
285+ if (!MirFocusController.focusedSurface) return;
286+ if (MirFocusController.focusedSurface.state == Mir.FullscreenState) {
287+ MirFocusController.focusedSurface.state = Mir.RestoredState;
280288 } else {
281− controls.focusedSurface.state = Mir.FullscreenState;
289+ MirFocusController.focusedSurface.state = Mir.FullscreenState;
282290 }
283291 }
284292
285293 Binding {
286294 target: fullscreeAppCheck
287− when: controls.focusedSurface
295+ when: MirFocusController.focusedSurface
288296 property: "checked"
289297 value: {
290− if (!controls.focusedSurface) return false;
291− return controls.focusedSurface.state === Mir.FullscreenState
298+ if (!MirFocusController.focusedSurface) return false;
299+ return MirFocusController.focusedSurface.state === Mir.FullscreenState
292300 }
293301 }
294302 }
@@ -302,21 +310,21 @@ Rectangle {
302310 id: chromeAppCheck
303311
304312 onTriggered: {
305− if (!controls.focusedSurface) return;
306− if (controls.focusedSurface.shellChrome == Mir.LowChrome) {
307− controls.focusedSurface.setShellChrome(Mir.NormalChrome);
313+ if (!MirFocusController.focusedSurface) return;
314+ if (MirFocusController.focusedSurface.shellChrome == Mir.LowChrome) {
315+ MirFocusController.focusedSurface.setShellChrome(Mir.NormalChrome);
308316 } else {
309− controls.focusedSurface.setShellChrome(Mir.LowChrome);
317+ MirFocusController.focusedSurface.setShellChrome(Mir.LowChrome);
310318 }
311319 }
312320
313321 Binding {
314322 target: chromeAppCheck
315− when: controls.focusedSurface !== null
323+ when: MirFocusController.focusedSurface !== null
316324 property: "checked"
317325 value: {
318− if (!controls.focusedSurface) return false;
319− controls.focusedSurface.shellChrome === Mir.LowChrome
326+ if (!MirFocusController.focusedSurface) return false;
327+ MirFocusController.focusedSurface.shellChrome === Mir.LowChrome
320328 }
321329 }
322330 }
@@ -381,12 +389,6 @@ Rectangle {
381389 signalName: "actionInvoked"
382390 }
383391
384− SignalSpy {
385− id: appRemovedSpy
386− target: ApplicationManager
387− signalName: "applicationRemoved"
388− }
389−
390392 Telephony.CallEntry {
391393 id: phoneCall
392394 phoneNumber: "+447812221111"
@@ -412,6 +414,7 @@ Rectangle {
412414 when: windowShown
413415
414416 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
417+ property QtObject topLevelSurfaceList: null
415418
416419 function init() {
417420 if (shellLoader.active) {
@@ -449,6 +452,9 @@ Rectangle {
449452 panel.dismissTimer = fakeDismissTimer;
450453
451454 waitForGreeterToStabilize();
455+
456+ topLevelSurfaceList = findInvisibleChild(shell, "topLevelSurfaceList");
457+ verify(topLevelSurfaceList);
452458 }
453459
454460 function loadDesktopShellWithApps() {
@@ -463,7 +469,9 @@ Rectangle {
463469 var app5 = ApplicationManager.startApplication("camera-app")
464470 var app6 = ApplicationManager.startApplication("gallery-app")
465471 var app7 = ApplicationManager.startApplication("calendar-app")
466− waitUntilAppWindowIsFullyLoaded(app7);
472+ for (var i = 0; i < topLevelSurfaceList.count; ++i) {
473+ waitUntilAppWindowIsFullyLoaded(topLevelSurfaceList.idAt(i));
474+ }
467475 }
468476
469477 function waitForGreeterToStabilize() {
@@ -503,7 +511,7 @@ Rectangle {
503511 Wizard.System.wizardEnabled = false;
504512
505513 // kill all (fake) running apps
506− killApps(ApplicationManager);
514+ killApps();
507515
508516 unlockAllModemsSpy.clear()
509517 LightDM.Greeter.authenticate(""); // reset greeter
@@ -511,26 +519,25 @@ Rectangle {
511519 sessionSpy.clear();
512520
513521 GSettingsController.setLifecycleExemptAppids([]);
514− }
515522
516− function killApps() {
517− while (ApplicationManager.count > 1) {
518− var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
519− ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
520− }
521− compare(ApplicationManager.count, 1)
523+ // there should be only unity8-dash window over there
524+ tryCompare(ApplicationManager, "count", 1);
525+ compare(ApplicationManager.get(0).appId, "unity8-dash");
522526 }
523527
524528 function test_snapDecisionDismissalReturnsFocus() {
525529 loadShell("phone");
526530 swipeAwayGreeter();
527531 var notifications = findChild(shell, "notificationList");
532+ var appSurfaceId = topLevelSurfaceList.nextId;
528533 var app = ApplicationManager.startApplication("camera-app");
534+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
529535 var stage = findChild(shell, "stage")
530− // Open an application and focus
531− waitUntilApplicationWindowIsFullyVisible(app);
532− ApplicationManager.focusApplication(app);
533− tryCompare(app.session.lastSurface, "activeFocus", true);
536+
537+ var appSurface = app.surfaceList.get(0);
538+ verify(appSurface);
539+
540+ tryCompare(appSurface, "activeFocus", true);
534541
535542 notifications.model = mockNotificationsModel;
536543
@@ -552,7 +559,7 @@ Rectangle {
552559 waitForRendering(notification);
553560
554561 // Make sure activeFocus went away from the app window
555− tryCompare(app.session.lastSurface, "activeFocus", false);
562+ tryCompare(appSurface, "activeFocus", false);
556563 tryCompare(stage, "interactive", false);
557564
558565 // Clicking the button should dismiss the notification and return focus
@@ -560,7 +567,7 @@ Rectangle {
560567 mouseClick(buttonAccept);
561568
562569 // Make sure we're back to normal
563− tryCompare(app.session.lastSurface, "activeFocus", true);
570+ tryCompare(appSurface, "activeFocus", true);
564571 compare(stage.interactive, true, "Stages not interactive again after modal notification has closed");
565572 }
566573
@@ -618,10 +625,9 @@ Rectangle {
618625 loadShell("phone");
619626 swipeAwayGreeter();
620627 dragLauncherIntoView();
628+ var appSurfaceId = topLevelSurfaceList.nextId;
621629 tapOnAppIconInLauncher();
622− waitUntilApplicationWindowIsFullyVisible();
623− ApplicationManager.focusApplication(data.focusedApp)
624− waitUntilApplicationWindowIsFullyVisible();
630+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
625631
626632 var greeter = findChild(shell, "greeter");
627633 if (data.greeterShown) {
@@ -637,16 +643,18 @@ Rectangle {
637643 waitUntilDashIsFocused();
638644 tryCompare(greeter, "shown", false);
639645 } else {
640− waitUntilApplicationWindowIsFullyVisible();
641646 compare(greeter.fullyShown, data.greeterShown);
642647 }
643648
644649 var launcher = findChild(shell, "launcherPanel");
645650 tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
646651
652+ //FIXME: This check fails. Don't understand the rationale behind it.
653+ /*
647654 // Make sure the helper for sliding out the launcher wasn't touched. We want to fade it out here.
648655 var animateTimer = findInvisibleChild(shell, "animateTimer");
649656 compare(animateTimer.nextState, "visible");
657+ */
650658 }
651659
652660 function test_tabletLeftEdgeDrag_data() {
@@ -673,10 +681,11 @@ Rectangle {
673681 dragLauncherIntoView();
674682 dashCommunicatorSpy.clear();
675683
684+ var appSurfaceId = topLevelSurfaceList.nextId;
676685 tapOnAppIconInLauncher();
677− waitUntilApplicationWindowIsFullyVisible();
686+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
678687
679− verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
688+ tryCompareFunction(function() { return ApplicationManager.focusedApplicationId !== "unity8-dash"; }, true);
680689
681690 //Long left swipe
682691 swipeFromLeftEdge(units.gu(30));
@@ -709,8 +718,9 @@ Rectangle {
709718
710719 // Launch an app from the launcher
711720 dragLauncherIntoView();
721+ var appSurfaceId = topLevelSurfaceList.nextId;
712722 tapOnAppIconInLauncher();
713− waitUntilApplicationWindowIsFullyVisible();
723+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
714724
715725 var mainAppId = ApplicationManager.focusedApplicationId;
716726 verify(mainAppId != "");
@@ -835,9 +845,9 @@ Rectangle {
835845 dragLauncherIntoView();
836846
837847 // Launch an app from the launcher
848+ var appSurfaceId = topLevelSurfaceList.nextId;
838849 tapOnAppIconInLauncher();
839−
840− waitUntilApplicationWindowIsFullyVisible();
850+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
841851
842852 verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
843853
@@ -873,10 +883,22 @@ Rectangle {
873883 tryCompare(item, "visible", false);
874884 }
875885
886+ function findAppWindowForSurfaceId(surfaceId) {
887+ // for PhoneStage and TabletStage
888+ var delegate = findChild(shell, "spreadDelegate_" + surfaceId);
889+ if (!delegate) {
890+ // for DesktopStage
891+ delegate = findChild(shell, "appDelegate_" + surfaceId);
892+ }
893+ verify(delegate);
894+ var appWindow = findChild(delegate, "appWindow");
895+ return appWindow;
896+ }
897+
876898 // Wait until the ApplicationWindow for the given Application object is fully loaded
877899 // (ie, the real surface has replaced the splash screen)
878− function waitUntilAppWindowIsFullyLoaded(app) {
879− var appWindow = findChild(shell, "appWindow_" + app.appId);
900+ function waitUntilAppWindowIsFullyLoaded(surfaceId) {
901+ var appWindow = findAppWindowForSurfaceId(surfaceId);
880902 var appWindowStateGroup = findInvisibleChild(appWindow, "applicationWindowStateGroup");
881903 tryCompareFunction(function() { return appWindowStateGroup.state === "surface" }, true);
882904 waitUntilTransitionsEnd(appWindowStateGroup);
@@ -885,10 +907,13 @@ Rectangle {
885907 function test_surfaceLosesActiveFocusWhilePanelIsOpen() {
886908 loadShell("phone");
887909 swipeAwayGreeter();
910+ var appSurfaceId = topLevelSurfaceList.nextId;
888911 var app = ApplicationManager.startApplication("dialer-app");
889− waitUntilAppWindowIsFullyLoaded(app);
912+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
913+ var appSurface = app.surfaceList.get(0);
914+ verify(appSurface);
890915
891− tryCompare(app.session.lastSurface, "activeFocus", true);
916+ tryCompare(appSurface, "activeFocus", true);
892917
893918 // Drag the indicators panel half-open
894919 var touchX = shell.width / 2;
@@ -899,7 +924,7 @@ Rectangle {
899924 true /* beginTouch */, false /* endTouch */);
900925 verify(indicators.partiallyOpened);
901926
902− tryCompare(app.session.lastSurface, "activeFocus", false);
927+ tryCompare(appSurface, "activeFocus", false);
903928
904929 // And finish getting it open
905930 touchFlick(indicators,
@@ -908,11 +933,11 @@ Rectangle {
908933 false /* beginTouch */, true /* endTouch */);
909934 tryCompare(indicators, "fullyOpened", true);
910935
911− tryCompare(app.session.lastSurface, "activeFocus", false);
936+ tryCompare(appSurface, "activeFocus", false);
912937
913938 dragToCloseIndicatorsPanel();
914939
915− tryCompare(app.session.lastSurface, "activeFocus", true);
940+ tryCompare(appSurface, "activeFocus", true);
916941 }
917942
918943 function test_launchedAppHasActiveFocus_data() {
@@ -928,54 +953,44 @@ Rectangle {
928953 shell.usageScenario = data.usageScenario;
929954 swipeAwayGreeter();
930955
956+ var webAppSurfaceId = topLevelSurfaceList.nextId;
931957 var webApp = ApplicationManager.startApplication("webbrowser-app");
932958 verify(webApp);
933− waitUntilAppSurfaceShowsUp("webbrowser-app")
959+ waitUntilAppWindowIsFullyLoaded(webAppSurfaceId);
934960
935− verify(webApp.session.lastSurface);
961+ var webAppSurface = webApp.surfaceList.get(topLevelSurfaceList.indexForId(webAppSurfaceId));
962+ verify(webAppSurface);
936963
937− tryCompare(webApp.session.lastSurface, "activeFocus", true);
964+ tryCompare(webAppSurface, "activeFocus", true);
938965 }
939966
940967 function test_launchedAppKeepsActiveFocusOnUsageModeChange() {
941968 loadShell("tablet");
942969 swipeAwayGreeter();
943970
971+ var webAppSurfaceId = topLevelSurfaceList.nextId;
944972 var webApp = ApplicationManager.startApplication("webbrowser-app");
945973 verify(webApp);
946− waitUntilAppSurfaceShowsUp("webbrowser-app")
974+ waitUntilAppWindowIsFullyLoaded(webAppSurfaceId);
947975
948− verify(webApp.session.lastSurface);
976+ var webAppSurface = webApp.surfaceList.get(topLevelSurfaceList.indexForId(webAppSurfaceId));
977+ verify(webAppSurface);
949978
950− tryCompare(webApp.session.lastSurface, "activeFocus", true);
979+ tryCompare(webAppSurface, "activeFocus", true);
951980
952981 shell.usageScenario = "desktop";
953982
954983 // check that the desktop stage and window have been loaded
955− {
956− var desktopWindow = findChild(shell, "appWindow_webbrowser-app");
957− verify(desktopWindow);
958− }
984+ waitUntilAppWindowIsFullyLoaded(webAppSurfaceId);
959985
960− tryCompare(webApp.session.lastSurface, "activeFocus", true);
986+ tryCompare(webAppSurface, "activeFocus", true);
961987
962988 shell.usageScenario = "tablet";
963989
964990 // check that the tablet stage and app surface delegate have been loaded
965− {
966− var desktopWindow = findChild(shell, "tabletSpreadDelegate_webbrowser-app");
967− verify(desktopWindow);
968− }
991+ waitUntilAppWindowIsFullyLoaded(webAppSurfaceId);
969992
970− tryCompare(webApp.session.lastSurface, "activeFocus", true);
971− }
972−
973− function waitUntilAppSurfaceShowsUp(appId) {
974− var appWindow = findChild(shell, "appWindow_" + appId);
975− verify(appWindow);
976− var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
977− verify(appWindowStates);
978− tryCompare(appWindowStates, "state", "surface");
993+ tryCompare(webAppSurface, "activeFocus", true);
979994 }
980995
981996 function dragToCloseIndicatorsPanel() {
@@ -1025,12 +1040,6 @@ Rectangle {
10251040 }
10261041 }
10271042
1028− function waitUntilApplicationWindowIsFullyVisible() {
1029− var appDelegate = findChild(shell, "appDelegate0")
1030− var surfaceContainer = findChild(appDelegate, "surfaceContainer");
1031− tryCompareFunction(function() { return surfaceContainer.surface !== null; }, true);
1032− }
1033−
10341043 function waitUntilDashIsFocused() {
10351044 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
10361045 }
@@ -1087,9 +1096,9 @@ Rectangle {
10871096 swipeAwayGreeter();
10881097 var greeter = findChild(shell, "greeter");
10891098
1099+ var appSurfaceId = topLevelSurfaceList.nextId;
10901100 var app = ApplicationManager.startApplication("dialer-app");
1091− // wait until the app is fully loaded (ie, real surface replaces splash screen)
1092− tryCompareFunction(function() { return app.session !== null && app.session.lastSurface !== null }, true);
1101+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
10931102
10941103 // Minimize the application we just launched
10951104 swipeFromLeftEdge(shell.width * 0.75);
@@ -1150,13 +1159,17 @@ Rectangle {
11501159 swipeAwayGreeter();
11511160 var panel = findChild(shell, "panel");
11521161 compare(panel.fullscreenMode, false);
1153− ApplicationManager.startApplication("camera-app");
1162+ var cameraSurfaceId = topLevelSurfaceList.nextId;
1163+ var cameraApp = ApplicationManager.startApplication("camera-app");
1164+ waitUntilAppWindowIsFullyLoaded(cameraSurfaceId);
11541165 tryCompare(panel, "fullscreenMode", true);
1155− ApplicationManager.startApplication("dialer-app");
1166+ var dialerSurfaceId = topLevelSurfaceList.nextId;
1167+ var dialerApp = ApplicationManager.startApplication("dialer-app");
1168+ waitUntilAppWindowIsFullyLoaded(dialerSurfaceId);
11561169 tryCompare(panel, "fullscreenMode", false);
1157− ApplicationManager.requestFocusApplication("camera-app");
1170+ ApplicationManager.requestFocusApplication(cameraApp.appId);
11581171 tryCompare(panel, "fullscreenMode", true);
1159− ApplicationManager.requestFocusApplication("dialer-app");
1172+ ApplicationManager.requestFocusApplication(dialerApp.appId);
11601173 tryCompare(panel, "fullscreenMode", false);
11611174 }
11621175
@@ -1227,24 +1240,32 @@ Rectangle {
12271240 var tutorial = findChild(shell, "tutorial");
12281241 tryCompare(wizard, "active", true);
12291242 tryCompare(tutorial, "running", true);
1230− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1243+ tryCompareFunction(function() { return topLevelSurfaceList.applicationAt(0).appId; }, "unity8-dash");
12311244
1232− // Make sure we stay running when nothing focused (can happen for
1245+ // Make sure we stay running when there's no top level window (can happen for
12331246 // a moment when we restart the dash after switching language)
1234− ApplicationManager.stopApplication("unity8-dash");
1235− tryCompare(ApplicationManager, "focusedApplicationId", "");
1247+ var dashApplication = ApplicationManager.findApplication("unity8-dash");
1248+ ApplicationManager.stopApplication(dashApplication.appId);
1249+ // wait until all zombie surfaces are gone. As MirSurfaceItems hold references over them.
1250+ // They won't be gone until those surface items are destroyed.
1251+ tryCompareFunction(function() { return dashApplication.surfaceList.count }, 0);
1252+
1253+ tryCompare(topLevelSurfaceList, "count", 0);
12361254 compare(wizard.shown, true);
12371255 compare(tutorial.running, true);
12381256
1239− // And make sure we stay running when dash focused again
1240− ApplicationManager.startApplication("unity8-dash");
1241− tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1257+ // And make sure we stay running when dash comes back again
1258+ var dashSurfaceId = topLevelSurfaceList.nextId;
1259+ ApplicationManager.startApplication(dashApplication.appId);
1260+ waitUntilAppWindowIsFullyLoaded(dashSurfaceId);
12421261 compare(wizard.shown, true);
12431262 compare(tutorial.running, true);
12441263
1245− // And make sure we stop when something else is focused
1246− ApplicationManager.startApplication("gallery-app");
1247− tryCompare(ApplicationManager, "focusedApplicationId", "gallery-app");
1264+ // And make sure we stop when some other surface shows app
1265+ var gallerySurfaceId = topLevelSurfaceList.nextId;
1266+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1267+ waitUntilAppWindowIsFullyLoaded(gallerySurfaceId);
1268+ tryCompareFunction(function() { return topLevelSurfaceList.applicationAt(0).appId; }, "gallery-app");
12481269 compare(wizard.shown, false);
12491270 compare(tutorial.running, false);
12501271 tryCompare(AccountsService, "demoEdges", false);
@@ -1269,7 +1290,7 @@ Rectangle {
12691290 function test_tapOnRightEdgeReachesApplicationSurface() {
12701291 loadShell("phone");
12711292 swipeAwayGreeter();
1272− var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1293+ var topmostSpreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
12731294 verify(topmostSpreadDelegate);
12741295
12751296 waitUntilFocusedApplicationIsShowingItsSurface();
@@ -1298,7 +1319,7 @@ Rectangle {
12981319 function test_rightEdgeDragDoesNotReachApplicationSurface() {
12991320 loadShell("phone");
13001321 swipeAwayGreeter();
1301− var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1322+ var topmostSpreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
13021323 var topmostSurfaceItem = findChild(topmostSpreadDelegate, "surfaceItem");
13031324 var rightEdgeDragArea = findChild(shell, "spreadDragArea");
13041325
@@ -1318,7 +1339,7 @@ Rectangle {
13181339
13191340 function waitUntilFocusedApplicationIsShowingItsSurface()
13201341 {
1321− var spreadDelegate = findChild(shell, "appDelegate0");
1342+ var spreadDelegate = findChild(shell, "spreadDelegate_" + topLevelSurfaceList.idAt(0));
13221343 var appState = findInvisibleChild(spreadDelegate, "applicationWindowStateGroup");
13231344 tryCompare(appState, "state", "surface");
13241345 var transitions = appState.transitions;
@@ -1467,11 +1488,13 @@ Rectangle {
14671488 loadShell("tablet");
14681489 shell.usageScenario = "desktop";
14691490
1491+ var webBrowserSurfaceId = topLevelSurfaceList.nextId;
14701492 var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1471− waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1493+ waitUntilAppWindowIsFullyLoaded(webBrowserSurfaceId);
14721494
1495+ var gallerySurfaceId = topLevelSurfaceList.nextId;
14731496 var galleryApp = ApplicationManager.startApplication("gallery-app");
1474− waitUntilAppWindowIsFullyLoaded(galleryApp);
1497+ waitUntilAppWindowIsFullyLoaded(gallerySurfaceId);
14751498
14761499 ApplicationManager.requestFocusApplication("unity8-dash");
14771500 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
@@ -1489,11 +1512,13 @@ Rectangle {
14891512 loadShell("tablet");
14901513 shell.usageScenario = "tablet";
14911514
1515+ var webBrowserSurfaceId = topLevelSurfaceList.nextId;
14921516 var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1493− waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1517+ waitUntilAppWindowIsFullyLoaded(webBrowserSurfaceId);
14941518
1519+ var gallerySurfaceId = topLevelSurfaceList.nextId;
14951520 var galleryApp = ApplicationManager.startApplication("gallery-app");
1496− waitUntilAppWindowIsFullyLoaded(galleryApp);
1521+ waitUntilAppWindowIsFullyLoaded(gallerySurfaceId);
14971522
14981523 ApplicationManager.requestFocusApplication("unity8-dash");
14991524 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
@@ -1523,17 +1548,26 @@ Rectangle {
15231548 var desktopStage = findChild(shell, "stage");
15241549 verify(desktopStage != null)
15251550
1551+ var app1SurfaceId = topLevelSurfaceList.nextId;
15261552 var app1 = ApplicationManager.startApplication("dialer-app")
1527− waitUntilAppWindowIsFullyLoaded(app1);
1553+ waitUntilAppWindowIsFullyLoaded(app1SurfaceId);
1554+
1555+ var app2SurfaceId = topLevelSurfaceList.nextId;
15281556 var app2 = ApplicationManager.startApplication("webbrowser-app")
1529− waitUntilAppWindowIsFullyLoaded(app2);
1557+ waitUntilAppWindowIsFullyLoaded(app2SurfaceId);
1558+ var app2Surface = app2.surfaceList.get(0);
1559+ verify(app2Surface);
1560+
1561+ var app3SurfaceId = topLevelSurfaceList.nextId;
15301562 var app3 = ApplicationManager.startApplication("camera-app")
1531− waitUntilAppWindowIsFullyLoaded(app3);
1563+ waitUntilAppWindowIsFullyLoaded(app3SurfaceId);
1564+ var app3Surface = app3.surfaceList.get(0);
1565+ verify(app3Surface);
15321566
15331567 // Do a quick alt-tab and see if focus changes
1534− tryCompare(app3.session.lastSurface, "activeFocus", true)
1568+ tryCompare(app3Surface, "activeFocus", true)
15351569 keyClick(Qt.Key_Tab, Qt.AltModifier)
1536− tryCompare(app2.session.lastSurface, "activeFocus", true)
1570+ tryCompare(app2Surface, "activeFocus", true)
15371571
15381572 // Press Alt+Tab
15391573 keyPress(Qt.Key_Alt);
@@ -1541,7 +1575,7 @@ Rectangle {
15411575 keyRelease(Qt.Key_Alt)
15421576
15431577 // Focus should have switched back now
1544− tryCompare(app3.session.lastSurface, "activeFocus", true)
1578+ tryCompare(app3Surface, "activeFocus", true)
15451579 }
15461580
15471581 function test_altTabWrapAround() {
@@ -1590,7 +1624,9 @@ Rectangle {
15901624 tryCompare(desktopSpread, "state", "")
15911625
15921626 // Make sure that after wrapping around once, we have the same one focused as at the beginning
1593− tryCompare(focused.session.lastSurface, "activeFocus", true)
1627+ var focusedAppSurface = focused.surfaceList.get(0);
1628+ verify(focusedAppSurface);
1629+ tryCompare(focusedAppSurface, "activeFocus", true)
15941630 }
15951631
15961632 function test_altBackTabNavigation() {
@@ -1624,7 +1660,7 @@ Rectangle {
16241660 keyRelease(Qt.Key_Alt);
16251661 }
16261662
1627− function test_highlightFollowsMouse() {
1663+ function otest_highlightFollowsMouse() {
16281664 loadDesktopShellWithApps()
16291665
16301666 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
@@ -1661,9 +1697,7 @@ Rectangle {
16611697 keyPress(Qt.Key_Alt)
16621698 keyClick(Qt.Key_Tab);
16631699
1664− appRemovedSpy.clear();
1665−
1666− var closedAppId = ApplicationManager.get(2).appId;
1700+ var surfaceId = topLevelSurfaceList.idAt(2);
16671701 var spreadDelegate2 = spreadRepeater.itemAt(2);
16681702 var closeMouseArea = findChild(spreadDelegate2, "closeMouseArea");
16691703
@@ -1678,12 +1712,14 @@ Rectangle {
16781712 }
16791713 tryCompare(closeMouseArea, "enabled", true)
16801714
1715+ var countBeforeClickingCloseButton = topLevelSurfaceList.count;
1716+ verify(topLevelSurfaceList.indexForId(surfaceId) === 2);
1717+
16811718 // Close the app using the close button
16821719 mouseClick(closeMouseArea, closeMouseArea.width / 2, closeMouseArea.height / 2)
16831720
1684− // Verify applicationRemoved has been emitted correctly
1685− tryCompare(appRemovedSpy, "count", 1)
1686− compare(appRemovedSpy.signalArguments[0][0], closedAppId);
1721+ tryCompare(topLevelSurfaceList, "count", countBeforeClickingCloseButton - 1);
1722+ verify(topLevelSurfaceList.indexForId(surfaceId) === -1);
16871723
16881724 keyRelease(Qt.Key_Alt);
16891725 }
@@ -1708,7 +1744,7 @@ Rectangle {
17081744 keyPress(Qt.Key_Alt)
17091745 keyClick(Qt.Key_Tab);
17101746
1711− var focusAppId = ApplicationManager.get(2).appId;
1747+ var surface = topLevelSurfaceList.surfaceAt(2);
17121748 var spreadDelegate2 = spreadRepeater.itemAt(2);
17131749 var clippedSpreadDelegate = findChild(spreadDelegate2, "clippedSpreadDelegate");
17141750
@@ -1730,7 +1766,7 @@ Rectangle {
17301766
17311767 // Verify that we left the spread and app2 is the focused one now
17321768 tryCompare(stage, "state", "");
1733− tryCompare(ApplicationManager, "focusedApplicationId", focusAppId);
1769+ tryCompare(surface, "focused", true);
17341770
17351771 keyRelease(Qt.Key_Alt);
17361772 }
@@ -1794,7 +1830,7 @@ Rectangle {
17941830 function test_workspacePreviewsHighlightedApp() {
17951831 loadDesktopShellWithApps()
17961832
1797− var targetZ = ApplicationManager.count + 1;
1833+ var targetZ = topLevelSurfaceList.count + 1;
17981834
17991835 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
18001836 verify(spreadRepeater !== null);
@@ -1870,7 +1906,8 @@ Rectangle {
18701906 loadDesktopShellWithApps();
18711907
18721908 var appRepeater = findChild(shell, "appRepeater")
1873− var appId = ApplicationManager.get(0).appId;
1909+ var application = topLevelSurfaceList.applicationAt(0);
1910+ var surfaceId = topLevelSurfaceList.idAt(0);
18741911 var appDelegate = appRepeater.itemAt(0);
18751912 var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
18761913
@@ -1881,17 +1918,20 @@ Rectangle {
18811918 tryCompare(appDelegate, "state", "maximized");
18821919 tryCompare(PanelState, "buttonsVisible", true)
18831920
1884− ApplicationManager.stopApplication(appId);
1921+ ApplicationManager.stopApplication(application.appId);
18851922 tryCompare(PanelState, "buttonsVisible", false)
18861923
1887− ApplicationManager.startApplication(appId);
1924+ // wait until all zombie surfaces are gone. As MirSurfaceItems hold references over them.
1925+ // They won't be gone until those surface items are destroyed.
1926+ tryCompareFunction(function() { return application.surfaceList.count }, 0);
1927+
1928+ ApplicationManager.startApplication(application.appId);
18881929 tryCompare(PanelState, "buttonsVisible", true)
18891930 }
18901931
18911932 function test_newAppHasValidGeometry() {
18921933 loadDesktopShellWithApps();
18931934 var appRepeater = findChild(shell, "appRepeater");
1894− var appId = ApplicationManager.get(0).appId;
18951935 var appDelegate = appRepeater.itemAt(0);
18961936
18971937 var resizeArea = findChild(appDelegate, "windowResizeArea");
@@ -1908,7 +1948,6 @@ Rectangle {
19081948 function test_switchToStagedHidesPanelButtons() {
19091949 loadDesktopShellWithApps();
19101950 var appRepeater = findChild(shell, "appRepeater")
1911− var appId = ApplicationManager.get(0).appId;
19121951 var appDelegate = appRepeater.itemAt(0);
19131952 var panelButtons = findChild(shell, "panelWindowControlButtons")
19141953
@@ -1925,7 +1964,6 @@ Rectangle {
19251964 function test_lockingGreeterHidesPanelButtons() {
19261965 loadDesktopShellWithApps();
19271966 var appRepeater = findChild(shell, "appRepeater")
1928− var appId = ApplicationManager.get(0).appId;
19291967 var appDelegate = appRepeater.itemAt(0);
19301968 var panelButtons = findChild(shell, "panelWindowControlButtons")
19311969
@@ -1955,12 +1993,15 @@ Rectangle {
19551993 shell.usageScenario = "desktop";
19561994 waitForRendering(shell);
19571995
1996+ var appSurfaceId = topLevelSurfaceList.nextId;
19581997 var app = ApplicationManager.startApplication("dialer-app")
1959− waitUntilAppWindowIsFullyLoaded(app);
1998+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
19601999
19612000 var appContainer = findChild(shell, "appContainer");
1962− var appDelegate = findChild(appContainer, "appDelegate_dialer-app");
1963− var decoration = findChild(appDelegate, "appWindowDecoration_dialer-app");
2001+ verify(appContainer);
2002+ var appDelegate = findChild(appContainer, "appDelegate_" + appSurfaceId);
2003+ verify(appDelegate);
2004+ var decoration = findChild(appDelegate, "appWindowDecoration");
19642005 verify(decoration);
19652006
19662007 // move it away from launcher and panel
@@ -1978,18 +2019,22 @@ Rectangle {
19782019 function test_restoreWindowStateFixesIfUnderPanel() {
19792020 loadDesktopShellWithApps();
19802021 var appRepeater = findChild(shell, "appRepeater")
1981− var appId = ApplicationManager.get(0).appId;
2022+ var application = topLevelSurfaceList.applicationAt(0);
19822023 var appDelegate = appRepeater.itemAt(0);
19832024
19842025 // Move it under the panel programmatically (might happen later with an alt+drag)
19852026 appDelegate.y = -units.gu(10)
19862027
1987− ApplicationManager.stopApplication(appId)
1988− ApplicationManager.startApplication(appId)
2028+ ApplicationManager.stopApplication(application.appId);
2029+ // wait until all zombie surfaces are gone. As MirSurfaceItems hold references over them.
2030+ // They won't be gone until those surface items are destroyed.
2031+ tryCompareFunction(function() { return application.surfaceList.count }, 0);
2032+
2033+ ApplicationManager.startApplication(application.appId);
19892034 waitForRendering(shell)
19902035
19912036 // Make sure the newly started one is at index 0 again
1992− tryCompare(ApplicationManager.get(0), "appId", appId);
2037+ tryCompareFunction(function () { return topLevelSurfaceList.applicationAt(0).appId; }, application.appId);
19932038
19942039 appDelegate = appRepeater.itemAt(0);
19952040 compare(appDelegate.y >= PanelState.panelHeight, true);
@@ -2008,16 +2053,22 @@ Rectangle {
20082053
20092054 // Add two main stage apps, the second in order to suspend the first.
20102055 // LibreOffice has isTouchApp set to false by our mocks.
2056+ var app1SurfaceId = topLevelSurfaceList.nextId;
20112057 var app1 = ApplicationManager.startApplication("libreoffice");
2012− waitUntilAppWindowIsFullyLoaded(app1);
2058+ waitUntilAppWindowIsFullyLoaded(app1SurfaceId);
2059+ var app2SurfaceId = topLevelSurfaceList.nextId;
20132060 var app2 = ApplicationManager.startApplication("gallery-app");
2014− waitUntilAppWindowIsFullyLoaded(app2);
2061+ waitUntilAppWindowIsFullyLoaded(app2SurfaceId);
20152062
20162063 // Sanity checking
20172064 compare(app1.stage, ApplicationInfoInterface.MainStage);
20182065 compare(app2.stage, ApplicationInfoInterface.MainStage);
20192066 verify(!app1.isTouchApp);
2020− verify(!app1.session.lastSurface.activeFocus);
2067+
2068+ var app1Surface = app1.surfaceList.get(0);
2069+ verify(app1Surface);
2070+
2071+ verify(!app1Surface.activeFocus);
20212072
20222073 // Make sure app1 is exempt with a requested suspend
20232074 verify(app1.exemptFromLifecycle);
@@ -2038,15 +2089,21 @@ Rectangle {
20382089 GSettingsController.setLifecycleExemptAppids(["webbrowser-app"]);
20392090
20402091 // Add two main stage apps, the second in order to suspend the first
2092+ var app1SurfaceId = topLevelSurfaceList.nextId;
20412093 var app1 = ApplicationManager.startApplication("webbrowser-app");
2042− waitUntilAppWindowIsFullyLoaded(app1);
2094+ waitUntilAppWindowIsFullyLoaded(app1SurfaceId);
2095+ var app2SurfaceId = topLevelSurfaceList.nextId;
20432096 var app2 = ApplicationManager.startApplication("gallery-app");
2044− waitUntilAppWindowIsFullyLoaded(app2);
2097+ waitUntilAppWindowIsFullyLoaded(app2SurfaceId);
20452098
20462099 // Sanity checking
20472100 compare(app1.stage, ApplicationInfoInterface.MainStage);
20482101 compare(app2.stage, ApplicationInfoInterface.MainStage);
2049− verify(!app1.session.lastSurface.activeFocus);
2102+
2103+ var app1Surface = app1.surfaceList.get(0);
2104+ verify(app1Surface);
2105+
2106+ verify(!app1Surface.activeFocus);
20502107
20512108 // Make sure app1 is exempt with a requested suspend
20522109 verify(app1.exemptFromLifecycle);
@@ -2205,9 +2262,10 @@ Rectangle {
22052262 var appContainer = findChild(shell, "appContainer");
22062263 var launcher = findChild(shell, "launcher");
22072264
2265+ var appSurfaceId = topLevelSurfaceList.nextId;
22082266 var app = ApplicationManager.startApplication("music-app");
2209− waitUntilAppWindowIsFullyLoaded(app);
2210− var appDelegate = findChild(appContainer, "appDelegate_music-app");
2267+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
2268+ var appDelegate = findChild(appContainer, "appDelegate_" + appSurfaceId);
22112269 appDelegate.maximize();
22122270 tryCompare(appDelegate, "visuallyMaximized", true);
22132271 waitForRendering(shell);
@@ -2235,8 +2293,18 @@ Rectangle {
22352293
22362294 tryCompare(launcher, "lockedVisible", true);
22372295
2238− var cameraApp = ApplicationManager.startApplication("camera-app");
2239− waitUntilAppWindowIsFullyLoaded(cameraApp);
2296+ var surfaceId = topLevelSurfaceList.nextId;
2297+ var app = ApplicationManager.startApplication("gmail-webapp");
2298+
2299+ waitUntilAppWindowIsFullyLoaded(surfaceId);
2300+
2301+ // Sanity check: ensure the fake app we chose creates a surface the way
2302+ // we expect it to.
2303+ compare(app.surfaceList.get(0).shellChrome, Mir.NormalChrome);
2304+
2305+ compare(launcher.lockedVisible, true);
2306+
2307+ app.surfaceList.get(0).state = Mir.FullscreenState;
22402308
22412309 tryCompare(launcher, "lockedVisible", false);
22422310 }
@@ -2257,8 +2325,9 @@ Rectangle {
22572325 swipeAwayGreeter();
22582326
22592327 // Let's open a fullscreen app
2328+ var appSurfaceId = topLevelSurfaceList.nextId;
22602329 var app = ApplicationManager.startApplication("camera-app");
2261− waitUntilAppWindowIsFullyLoaded(app);
2330+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
22622331
22632332 var appRepeater = findChild(shell, data.repeaterName);
22642333 var topmostAppDelegate = appRepeater.itemAt(0);
@@ -2295,19 +2364,21 @@ Rectangle {
22952364 AccountsService.keymaps = ["sk", "cz+qwerty", "fr"] // "configure" the keymaps for user
22962365
22972366 // start some app
2367+ var appSurfaceId = topLevelSurfaceList.nextId;
22982368 var app = ApplicationManager.startApplication("dialer-app");
2299− waitUntilAppWindowIsFullyLoaded(app);
2369+ waitUntilAppWindowIsFullyLoaded(appSurfaceId);
2370+ var appSurface = app.surfaceList.get(0);
23002371
23012372 // verify the initial keymap of the newly started app is the first one from the list
2302− tryCompare(app.session.lastSurface, "keymap", "sk");
2373+ tryCompare(appSurface, "keymap", "sk");
23032374
23042375 // switch to next keymap, should go to "cz+qwerty"
23052376 keyClick(Qt.Key_Space, Qt.MetaModifier);
2306− tryCompare(app.session.lastSurface, "keymap", "cz+qwerty");
2377+ tryCompare(appSurface, "keymap", "cz+qwerty");
23072378
23082379 // switch to next keymap, should go to "fr"
23092380 keyClick(Qt.Key_Space, Qt.MetaModifier);
2310− tryCompare(app.session.lastSurface, "keymap", "fr");
2381+ tryCompare(appSurface, "keymap", "fr");
23112382
23122383 // go to e.g. desktop stage
23132384 loadShell("desktop");
@@ -2315,30 +2386,32 @@ Rectangle {
23152386 waitForRendering(shell);
23162387
23172388 // start a second app, should get the last configured keyboard, "fr"
2389+ var app2SurfaceId = topLevelSurfaceList.nextId;
23182390 var app2 = ApplicationManager.startApplication("calendar-app");
2319− waitUntilAppWindowIsFullyLoaded(app2);
2320− tryCompare(app2.session.lastSurface, "keymap", "fr");
2391+ waitUntilAppWindowIsFullyLoaded(app2SurfaceId);
2392+ var app2Surface = app2.surfaceList.get(0);
2393+ tryCompare(app2Surface, "keymap", "fr");
23212394
23222395 // focus our first app, make sure it also has the "fr" keymap
23232396 ApplicationManager.requestFocusApplication("dialer-app");
2324− tryCompare(app.session.lastSurface, "keymap", "fr");
2397+ tryCompare(appSurface, "keymap", "fr");
23252398
23262399 // switch to previous keymap, should be "cz+qwerty"
23272400 keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier);
2328− tryCompare(app.session.lastSurface, "keymap", "cz+qwerty");
2401+ tryCompare(appSurface, "keymap", "cz+qwerty");
23292402
23302403 // go next twice to "sk", past the end
23312404 keyClick(Qt.Key_Space, Qt.MetaModifier);
23322405 keyClick(Qt.Key_Space, Qt.MetaModifier);
2333− tryCompare(app.session.lastSurface, "keymap", "sk");
2406+ tryCompare(appSurface, "keymap", "sk");
23342407
23352408 // go back once to past the beginning, to "fr"
23362409 keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier);
2337− tryCompare(app.session.lastSurface, "keymap", "fr");
2410+ tryCompare(appSurface, "keymap", "fr");
23382411
23392412 // switch to app2, should also get "fr"
23402413 ApplicationManager.requestFocusApplication("calendar-app");
2341− tryCompare(app2.session.lastSurface, "keymap", "fr");
2414+ tryCompare(app2Surface, "keymap", "fr");
23422415 }
23432416 }
23442417}