Use StopChildProcess instead of base::KillProcess to kill a renderer process
[chromium-blink-merge.git] / chrome / browser / extensions / extension_crash_recovery_browsertest.cc
blob9a40df84c1c27cb84de9bf816976be6a3a10dce7
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
3 // found in the LICENSE file.
5 #include "chrome/browser/browser_process.h"
6 #include "chrome/browser/extensions/extension_browsertest.h"
7 #include "chrome/browser/extensions/extension_service.h"
8 #include "chrome/browser/notifications/notification.h"
9 #include "chrome/browser/notifications/notification_delegate.h"
10 #include "chrome/browser/notifications/notification_ui_manager.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_commands.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "content/public/browser/navigation_controller.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/result_codes.h"
21 #include "content/public/common/url_constants.h"
22 #include "extensions/browser/extension_host.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/process_manager.h"
26 #include "extensions/browser/process_map.h"
27 #include "extensions/common/constants.h"
28 #include "ui/message_center/message_center.h"
29 #include "ui/message_center/notification_list.h"
31 using content::NavigationController;
32 using content::WebContents;
33 using extensions::Extension;
34 using extensions::ExtensionRegistry;
36 // Tests are timing out waiting for extension to crash.
37 // http://crbug.com/174705
38 #if defined(OS_MACOSX) || defined(USE_AURA) || defined(OS_LINUX)
39 #define MAYBE_ExtensionCrashRecoveryTest DISABLED_ExtensionCrashRecoveryTest
40 #else
41 #define MAYBE_ExtensionCrashRecoveryTest ExtensionCrashRecoveryTest
42 #endif // defined(OS_MACOSX) || defined(USE_AURA) || defined(OS_LINUX)
44 class ExtensionCrashRecoveryTestBase : public ExtensionBrowserTest {
45 protected:
46 virtual void AcceptNotification(size_t index) = 0;
47 virtual void CancelNotification(size_t index) = 0;
48 virtual size_t CountBalloons() = 0;
50 ExtensionService* GetExtensionService() {
51 return extensions::ExtensionSystem::Get(browser()->profile())->
52 extension_service();
55 extensions::ProcessManager* GetProcessManager() {
56 return extensions::ProcessManager::Get(browser()->profile());
59 ExtensionRegistry* GetExtensionRegistry() {
60 return ExtensionRegistry::Get(browser()->profile());
63 size_t GetEnabledExtensionCount() {
64 return GetExtensionRegistry()->enabled_extensions().size();
67 size_t GetTerminatedExtensionCount() {
68 return GetExtensionRegistry()->terminated_extensions().size();
71 void CrashExtension(const std::string& extension_id) {
72 const Extension* extension = GetExtensionRegistry()->GetExtensionById(
73 extension_id, ExtensionRegistry::ENABLED);
74 ASSERT_TRUE(extension);
75 extensions::ExtensionHost* extension_host = GetProcessManager()->
76 GetBackgroundHostForExtension(extension_id);
77 ASSERT_TRUE(extension_host);
79 extension_host->render_process_host()->Shutdown(content::RESULT_CODE_KILLED,
80 false);
81 ASSERT_TRUE(WaitForExtensionCrash(extension_id));
82 ASSERT_FALSE(GetProcessManager()->
83 GetBackgroundHostForExtension(extension_id));
85 // Wait for extension crash balloon to appear.
86 base::MessageLoop::current()->RunUntilIdle();
89 void CheckExtensionConsistency(const std::string& extension_id) {
90 const Extension* extension = GetExtensionRegistry()->GetExtensionById(
91 extension_id, ExtensionRegistry::ENABLED);
92 ASSERT_TRUE(extension);
93 extensions::ExtensionHost* extension_host = GetProcessManager()->
94 GetBackgroundHostForExtension(extension_id);
95 ASSERT_TRUE(extension_host);
96 extensions::ProcessManager::ViewSet all_views =
97 GetProcessManager()->GetAllViews();
98 extensions::ProcessManager::ViewSet::const_iterator it =
99 all_views.find(extension_host->host_contents()->GetRenderViewHost());
100 ASSERT_FALSE(it == all_views.end());
101 ASSERT_TRUE(extension_host->IsRenderViewLive());
102 extensions::ProcessMap* process_map =
103 extensions::ProcessMap::Get(browser()->profile());
104 ASSERT_TRUE(process_map->Contains(
105 extension_id,
106 extension_host->render_view_host()->GetProcess()->GetID()));
109 void LoadTestExtension() {
110 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
111 const Extension* extension = LoadExtension(
112 test_data_dir_.AppendASCII("common").AppendASCII("background_page"));
113 ASSERT_TRUE(extension);
114 first_extension_id_ = extension->id();
115 CheckExtensionConsistency(first_extension_id_);
118 void LoadSecondExtension() {
119 const Extension* extension = LoadExtension(
120 test_data_dir_.AppendASCII("install").AppendASCII("install"));
121 ASSERT_TRUE(extension);
122 second_extension_id_ = extension->id();
123 CheckExtensionConsistency(second_extension_id_);
126 std::string first_extension_id_;
127 std::string second_extension_id_;
130 class MAYBE_ExtensionCrashRecoveryTest : public ExtensionCrashRecoveryTestBase {
131 protected:
132 void AcceptNotification(size_t index) override {
133 message_center::MessageCenter* message_center =
134 message_center::MessageCenter::Get();
135 ASSERT_GT(message_center->NotificationCount(), index);
136 message_center::NotificationList::Notifications::reverse_iterator it =
137 message_center->GetVisibleNotifications().rbegin();
138 for (size_t i = 0; i < index; ++i)
139 ++it;
140 std::string id = (*it)->id();
141 message_center->ClickOnNotification(id);
142 WaitForExtensionLoad();
145 void CancelNotification(size_t index) override {
146 message_center::MessageCenter* message_center =
147 message_center::MessageCenter::Get();
148 ASSERT_GT(message_center->NotificationCount(), index);
149 message_center::NotificationList::Notifications::reverse_iterator it =
150 message_center->GetVisibleNotifications().rbegin();
151 for (size_t i = 0; i < index; ++i)
152 ++it;
153 ASSERT_TRUE(g_browser_process->notification_ui_manager()->CancelById(
154 (*it)->id(),
155 NotificationUIManager::GetProfileID(browser()->profile())));
158 size_t CountBalloons() override {
159 return message_center::MessageCenter::Get()->NotificationCount();
163 // Flaky: http://crbug.com/242167.
164 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest, DISABLED_Basic) {
165 const size_t count_before = GetEnabledExtensionCount();
166 const size_t crash_count_before = GetTerminatedExtensionCount();
167 LoadTestExtension();
168 CrashExtension(first_extension_id_);
169 ASSERT_EQ(count_before, GetEnabledExtensionCount());
170 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
171 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
173 SCOPED_TRACE("after clicking the balloon");
174 CheckExtensionConsistency(first_extension_id_);
175 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
178 // Flaky, http://crbug.com/241191.
179 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
180 DISABLED_CloseAndReload) {
181 const size_t count_before = GetEnabledExtensionCount();
182 const size_t crash_count_before = GetTerminatedExtensionCount();
183 LoadTestExtension();
184 CrashExtension(first_extension_id_);
186 ASSERT_EQ(count_before, GetEnabledExtensionCount());
187 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
189 ASSERT_NO_FATAL_FAILURE(CancelNotification(0));
190 ReloadExtension(first_extension_id_);
192 SCOPED_TRACE("after reloading");
193 CheckExtensionConsistency(first_extension_id_);
194 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
197 // Test is timing out on Windows http://crbug.com/174705.
198 #if defined(OS_WIN)
199 #define MAYBE_ReloadIndependently DISABLED_ReloadIndependently
200 #else
201 #define MAYBE_ReloadIndependently ReloadIndependently
202 #endif // defined(OS_WIN)
203 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
204 MAYBE_ReloadIndependently) {
205 const size_t count_before = GetEnabledExtensionCount();
206 LoadTestExtension();
207 CrashExtension(first_extension_id_);
208 ASSERT_EQ(count_before, GetEnabledExtensionCount());
210 ReloadExtension(first_extension_id_);
212 SCOPED_TRACE("after reloading");
213 CheckExtensionConsistency(first_extension_id_);
215 WebContents* current_tab =
216 browser()->tab_strip_model()->GetActiveWebContents();
217 ASSERT_TRUE(current_tab);
219 // The balloon should automatically hide after the extension is successfully
220 // reloaded.
221 ASSERT_EQ(0U, CountBalloons());
224 // Test is timing out on Windows http://crbug.com/174705.
225 #if defined(OS_WIN)
226 #define MAYBE_ReloadIndependentlyChangeTabs DISABLED_ReloadIndependentlyChangeTabs
227 #else
228 #define MAYBE_ReloadIndependentlyChangeTabs ReloadIndependentlyChangeTabs
229 #endif // defined(OS_WIN)
231 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
232 MAYBE_ReloadIndependentlyChangeTabs) {
233 const size_t count_before = GetEnabledExtensionCount();
234 LoadTestExtension();
235 CrashExtension(first_extension_id_);
236 ASSERT_EQ(count_before, GetEnabledExtensionCount());
238 WebContents* original_tab =
239 browser()->tab_strip_model()->GetActiveWebContents();
240 ASSERT_TRUE(original_tab);
241 ASSERT_EQ(1U, CountBalloons());
243 // Open a new tab, but the balloon will still be there.
244 chrome::NewTab(browser());
245 WebContents* new_current_tab =
246 browser()->tab_strip_model()->GetActiveWebContents();
247 ASSERT_TRUE(new_current_tab);
248 ASSERT_NE(new_current_tab, original_tab);
249 ASSERT_EQ(1U, CountBalloons());
251 ReloadExtension(first_extension_id_);
253 SCOPED_TRACE("after reloading");
254 CheckExtensionConsistency(first_extension_id_);
256 // The balloon should automatically hide after the extension is successfully
257 // reloaded.
258 ASSERT_EQ(0U, CountBalloons());
261 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
262 DISABLED_ReloadIndependentlyNavigatePage) {
263 const size_t count_before = GetEnabledExtensionCount();
264 LoadTestExtension();
265 CrashExtension(first_extension_id_);
266 ASSERT_EQ(count_before, GetEnabledExtensionCount());
268 WebContents* current_tab =
269 browser()->tab_strip_model()->GetActiveWebContents();
270 ASSERT_TRUE(current_tab);
271 ASSERT_EQ(1U, CountBalloons());
273 // Navigate to another page.
274 ui_test_utils::NavigateToURL(
275 browser(), ui_test_utils::GetTestUrl(
276 base::FilePath(base::FilePath::kCurrentDirectory),
277 base::FilePath(FILE_PATH_LITERAL("title1.html"))));
278 ASSERT_EQ(1U, CountBalloons());
280 ReloadExtension(first_extension_id_);
282 SCOPED_TRACE("after reloading");
283 CheckExtensionConsistency(first_extension_id_);
285 // The balloon should automatically hide after the extension is successfully
286 // reloaded.
287 ASSERT_EQ(0U, CountBalloons());
290 // Make sure that when we don't do anything about the crashed extension
291 // and close the browser, it doesn't crash. The browser is closed implicitly
292 // at the end of each browser test.
294 // http://crbug.com/84719
295 #if defined(OS_LINUX)
296 #define MAYBE_ShutdownWhileCrashed DISABLED_ShutdownWhileCrashed
297 #else
298 #define MAYBE_ShutdownWhileCrashed ShutdownWhileCrashed
299 #endif // defined(OS_LINUX)
301 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
302 MAYBE_ShutdownWhileCrashed) {
303 const size_t count_before = GetEnabledExtensionCount();
304 LoadTestExtension();
305 CrashExtension(first_extension_id_);
306 ASSERT_EQ(count_before, GetEnabledExtensionCount());
309 // Flaky, http://crbug.com/241245.
310 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
311 DISABLED_TwoExtensionsCrashFirst) {
312 const size_t count_before = GetEnabledExtensionCount();
313 LoadTestExtension();
314 LoadSecondExtension();
315 CrashExtension(first_extension_id_);
316 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
317 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
319 SCOPED_TRACE("after clicking the balloon");
320 CheckExtensionConsistency(first_extension_id_);
321 CheckExtensionConsistency(second_extension_id_);
324 // Flaky: http://crbug.com/242196
325 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
326 DISABLED_TwoExtensionsCrashSecond) {
327 const size_t count_before = GetEnabledExtensionCount();
328 LoadTestExtension();
329 LoadSecondExtension();
330 CrashExtension(second_extension_id_);
331 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
332 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
334 SCOPED_TRACE("after clicking the balloon");
335 CheckExtensionConsistency(first_extension_id_);
336 CheckExtensionConsistency(second_extension_id_);
339 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
340 TwoExtensionsCrashBothAtOnce) {
341 const size_t count_before = GetEnabledExtensionCount();
342 const size_t crash_count_before = GetTerminatedExtensionCount();
343 LoadTestExtension();
344 LoadSecondExtension();
345 CrashExtension(first_extension_id_);
346 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
347 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
348 CrashExtension(second_extension_id_);
349 ASSERT_EQ(count_before, GetEnabledExtensionCount());
350 ASSERT_EQ(crash_count_before + 2, GetTerminatedExtensionCount());
353 SCOPED_TRACE("first balloon");
354 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
355 CheckExtensionConsistency(first_extension_id_);
359 SCOPED_TRACE("second balloon");
360 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
361 CheckExtensionConsistency(first_extension_id_);
362 CheckExtensionConsistency(second_extension_id_);
366 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
367 TwoExtensionsOneByOne) {
368 const size_t count_before = GetEnabledExtensionCount();
369 LoadTestExtension();
370 CrashExtension(first_extension_id_);
371 ASSERT_EQ(count_before, GetEnabledExtensionCount());
372 LoadSecondExtension();
373 CrashExtension(second_extension_id_);
374 ASSERT_EQ(count_before, GetEnabledExtensionCount());
377 SCOPED_TRACE("first balloon");
378 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
379 CheckExtensionConsistency(first_extension_id_);
383 SCOPED_TRACE("second balloon");
384 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
385 CheckExtensionConsistency(first_extension_id_);
386 CheckExtensionConsistency(second_extension_id_);
390 // http://crbug.com/84719
391 #if defined(OS_LINUX)
392 #define MAYBE_TwoExtensionsShutdownWhileCrashed \
393 DISABLED_TwoExtensionsShutdownWhileCrashed
394 #else
395 #define MAYBE_TwoExtensionsShutdownWhileCrashed \
396 TwoExtensionsShutdownWhileCrashed
397 #endif // defined(OS_LINUX)
399 // Make sure that when we don't do anything about the crashed extensions
400 // and close the browser, it doesn't crash. The browser is closed implicitly
401 // at the end of each browser test.
402 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
403 MAYBE_TwoExtensionsShutdownWhileCrashed) {
404 const size_t count_before = GetEnabledExtensionCount();
405 LoadTestExtension();
406 CrashExtension(first_extension_id_);
407 ASSERT_EQ(count_before, GetEnabledExtensionCount());
408 LoadSecondExtension();
409 CrashExtension(second_extension_id_);
410 ASSERT_EQ(count_before, GetEnabledExtensionCount());
413 // Flaky, http://crbug.com/241573.
414 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
415 DISABLED_TwoExtensionsIgnoreFirst) {
416 const size_t count_before = GetEnabledExtensionCount();
417 LoadTestExtension();
418 LoadSecondExtension();
419 CrashExtension(first_extension_id_);
420 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
421 CrashExtension(second_extension_id_);
422 ASSERT_EQ(count_before, GetEnabledExtensionCount());
424 // Accept notification 1 before canceling notification 0.
425 // Otherwise, on Linux and Windows, there is a race here, in which
426 // canceled notifications do not immediately go away.
427 ASSERT_NO_FATAL_FAILURE(AcceptNotification(1));
428 ASSERT_NO_FATAL_FAILURE(CancelNotification(0));
430 SCOPED_TRACE("balloons done");
431 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
432 CheckExtensionConsistency(second_extension_id_);
435 // Flaky, http://crbug.com/241164.
436 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
437 DISABLED_TwoExtensionsReloadIndependently) {
438 const size_t count_before = GetEnabledExtensionCount();
439 LoadTestExtension();
440 LoadSecondExtension();
441 CrashExtension(first_extension_id_);
442 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
443 CrashExtension(second_extension_id_);
444 ASSERT_EQ(count_before, GetEnabledExtensionCount());
447 SCOPED_TRACE("first: reload");
448 WebContents* current_tab =
449 browser()->tab_strip_model()->GetActiveWebContents();
450 ASSERT_TRUE(current_tab);
451 // At the beginning we should have one balloon displayed for each extension.
452 ASSERT_EQ(2U, CountBalloons());
453 ReloadExtension(first_extension_id_);
454 // One of the balloons should hide after the extension is reloaded.
455 ASSERT_EQ(1U, CountBalloons());
456 CheckExtensionConsistency(first_extension_id_);
460 SCOPED_TRACE("second: balloon");
461 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
462 CheckExtensionConsistency(first_extension_id_);
463 CheckExtensionConsistency(second_extension_id_);
467 // http://crbug.com/243648
468 #if defined(OS_WIN)
469 #define MAYBE_CrashAndUninstall DISABLED_CrashAndUninstall
470 #else
471 #define MAYBE_CrashAndUninstall CrashAndUninstall
472 #endif
473 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
474 MAYBE_CrashAndUninstall) {
475 const size_t count_before = GetEnabledExtensionCount();
476 const size_t crash_count_before = GetTerminatedExtensionCount();
477 LoadTestExtension();
478 LoadSecondExtension();
479 CrashExtension(first_extension_id_);
480 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
481 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
483 ASSERT_EQ(1U, CountBalloons());
484 UninstallExtension(first_extension_id_);
485 base::MessageLoop::current()->RunUntilIdle();
487 SCOPED_TRACE("after uninstalling");
488 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
489 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
490 ASSERT_EQ(0U, CountBalloons());
493 // http://crbug.com/84719
494 #if defined(OS_LINUX)
495 #define MAYBE_CrashAndUnloadAll DISABLED_CrashAndUnloadAll
496 #else
497 #define MAYBE_CrashAndUnloadAll CrashAndUnloadAll
498 #endif // defined(OS_LINUX)
500 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
501 MAYBE_CrashAndUnloadAll) {
502 const size_t count_before = GetEnabledExtensionCount();
503 const size_t crash_count_before = GetTerminatedExtensionCount();
504 LoadTestExtension();
505 LoadSecondExtension();
506 CrashExtension(first_extension_id_);
507 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
508 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
510 GetExtensionService()->UnloadAllExtensionsForTest();
511 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
514 // Fails a DCHECK on Aura and Linux: http://crbug.com/169622
515 // Failing on Windows: http://crbug.com/232340
516 #if defined(USE_AURA)
517 #define MAYBE_ReloadTabsWithBackgroundPage DISABLED_ReloadTabsWithBackgroundPage
518 #else
519 #define MAYBE_ReloadTabsWithBackgroundPage ReloadTabsWithBackgroundPage
520 #endif
522 // Test that when an extension with a background page that has a tab open
523 // crashes, the tab stays open, and reloading it reloads the extension.
524 // Regression test for issue 71629.
525 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
526 MAYBE_ReloadTabsWithBackgroundPage) {
527 TabStripModel* tab_strip = browser()->tab_strip_model();
528 const size_t count_before = GetEnabledExtensionCount();
529 const size_t crash_count_before = GetTerminatedExtensionCount();
530 LoadTestExtension();
532 // Open a tab extension.
533 chrome::NewTab(browser());
534 ui_test_utils::NavigateToURL(browser(),
535 GURL(std::string(extensions::kExtensionScheme) +
536 url::kStandardSchemeSeparator +
537 first_extension_id_ + "/background.html"));
539 const int tabs_before = tab_strip->count();
540 CrashExtension(first_extension_id_);
542 // Tab should still be open, and extension should be crashed.
543 EXPECT_EQ(tabs_before, tab_strip->count());
544 EXPECT_EQ(count_before, GetEnabledExtensionCount());
545 EXPECT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
548 content::WindowedNotificationObserver observer(
549 content::NOTIFICATION_LOAD_STOP,
550 content::Source<NavigationController>(
551 &browser()->tab_strip_model()->GetActiveWebContents()->
552 GetController()));
553 chrome::Reload(browser(), CURRENT_TAB);
554 observer.Wait();
556 // Extension should now be loaded.
557 SCOPED_TRACE("after reloading the tab");
558 CheckExtensionConsistency(first_extension_id_);
559 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
560 ASSERT_EQ(0U, CountBalloons());