// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/notifications/balloon_collection_impl.h"
#include "chrome/browser/chromeos/notifications/balloon_view.h"
#include "chrome/browser/chromeos/notifications/notification_panel.h"
#include "chrome/browser/chromeos/notifications/system_notification_factory.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "content/common/notification_service.h"
#include "ui/base/x/x11_util.h"

namespace {

// The name of ChromeOS's window manager.
const char* kChromeOsWindowManagerName = "chromeos-wm";

}  // namespace

namespace chromeos {

class NotificationTest : public InProcessBrowserTest,
                         public NotificationObserver {
 public:
  NotificationTest()
      : under_chromeos_(false),
        state_(PanelController::INITIAL),
        expected_(PanelController::INITIAL) {
  }

  void HandleWebUIMessage(const ListValue* value) {
    MessageLoop::current()->Quit();
  }

 protected:
  virtual void SetUp() {
    // Detect if we're running under ChromeOS WindowManager. See
    // the description for "under_chromeos_" below for why we need this.
    std::string wm_name;
    bool wm_name_valid = ui::GetWindowManagerName(&wm_name);
    // NOTE: On Chrome OS the wm and Chrome are started in parallel. This
    // means it's possible for us not to be able to get the name of the window
    // manager. We assume that when this happens we're on Chrome OS.
    under_chromeos_ = (!wm_name_valid ||
                       wm_name == kChromeOsWindowManagerName);
    InProcessBrowserTest::SetUp();
  }

  BalloonCollectionImpl* GetBalloonCollectionImpl() {
    return static_cast<BalloonCollectionImpl*>(
        g_browser_process->notification_ui_manager()->balloon_collection());
  }

  NotificationPanel* GetNotificationPanel() {
    return static_cast<NotificationPanel*>(
        GetBalloonCollectionImpl()->notification_ui());
  }

  Notification NewMockNotification(const std::string& id) {
    return NewMockNotification(new MockNotificationDelegate(id));
  }

  Notification NewMockNotification(NotificationDelegate* delegate) {
    std::string text = delegate->id();
    return SystemNotificationFactory::Create(
        GURL(), ASCIIToUTF16(text.c_str()), ASCIIToUTF16(text.c_str()),
        delegate);
  }

  void MarkStale(const char* id) {
    GetNotificationPanel()->GetTester()->MarkStale(NewMockNotification(id));
  }

  // Waits untilt the panel's state becomes the specified state.
  // Does nothing if it's not running with ChromeOS Window Manager.
  void WaitForPanelState(NotificationPanelTester* tester,
                         PanelController::State state) {
    if (under_chromeos_ && state != state_) {
      expected_ = state;
      ui_test_utils::RunAllPendingInMessageLoop();
    }
  }

  // Busy loop to wait until the view becomes visible in the panel.
  void WaitForVisible(BalloonViewImpl* view) {
    WaitForResize(view);
    NotificationPanelTester* tester = GetNotificationPanel()->GetTester();
    while (!tester->IsVisible(view)) {
      ui_test_utils::RunAllPendingInMessageLoop();
    }
  }

  // Busy loop to wait until the webkit give some size to the notification.
  void WaitForResize(BalloonViewImpl* view) {
    while (view->bounds().IsEmpty()) {
      ui_test_utils::RunAllPendingInMessageLoop();
    }
  }

  // NotificationObserver overrides.
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    ASSERT_TRUE(NotificationType::PANEL_STATE_CHANGED == type);
    PanelController::State* state =
        reinterpret_cast<PanelController::State*>(details.map_key());
    state_ = *state;
    if (under_chromeos_ && expected_ == state_) {
      expected_ = PanelController::INITIAL;
      MessageLoop::current()->Quit();
    }
  }

 private:
  // ChromeOS build of chrome communicates with ChromeOS's
  // WindowManager, and behaves differently if it runs under a
  // chromeos window manager.  ChromeOS WindowManager sends
  // EXPANDED/MINIMIED state change message when the panels's state
  // changed (regardless of who changed it), and to avoid
  // mis-recognizing such events as user-initiated actions, we need to
  // wait and eat them before moving to a next step.
  bool under_chromeos_;
  PanelController::State state_;
  PanelController::State expected_;
};

IN_PROC_BROWSER_TEST_F(NotificationTest, TestBasic) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();

  // Using system notification as regular notification.
  collection->Add(NewMockNotification("1"), browser()->profile());

  EXPECT_EQ(1, tester->GetNewNotificationCount());
  EXPECT_EQ(1, tester->GetNotificationCount());
  EXPECT_EQ(0, tester->GetStickyNotificationCount());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  collection->Add(NewMockNotification("2"), browser()->profile());

  EXPECT_EQ(2, tester->GetNewNotificationCount());
  EXPECT_EQ(2, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  collection->RemoveById("1");
  ui_test_utils::RunAllPendingInMessageLoop();

  EXPECT_EQ(1, tester->GetNewNotificationCount());
  EXPECT_EQ(1, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  collection->RemoveById("2");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(0, tester->GetNewNotificationCount());
  EXPECT_EQ(0, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());

  // CLOSE is asynchronous. Run the all pending tasks to finish closing
  // task.
  ui_test_utils::RunAllPendingInMessageLoop();
}

// [CLOSED] -add->[STICKY_AND_NEW] -mouse-> [KEEP_SIZE] -remove/add->
// [KEEP_SIZE] -remove-> [CLOSED] -add-> [STICKY_AND_NEW] -remove-> [CLOSED]
IN_PROC_BROWSER_TEST_F(NotificationTest, TestKeepSizeState) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();

  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());

  // Using system notification as regular notification.
  collection->Add(NewMockNotification("1"), browser()->profile());
  collection->Add(NewMockNotification("2"), browser()->profile());

  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  panel->OnMouseMotion(gfx::Point(10, 10));
  EXPECT_EQ(NotificationPanel::KEEP_SIZE, tester->state());

  collection->RemoveById("1");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(1, tester->GetNewNotificationCount());
  EXPECT_EQ(1, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::KEEP_SIZE, tester->state());

  collection->Add(NewMockNotification("1"), browser()->profile());
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(2, tester->GetNewNotificationCount());
  EXPECT_EQ(2, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::KEEP_SIZE, tester->state());

  collection->RemoveById("1");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(1, tester->GetNewNotificationCount());
  EXPECT_EQ(1, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::KEEP_SIZE, tester->state());

  collection->RemoveById("2");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(0, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());

  collection->Add(NewMockNotification("3"), browser()->profile());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
  collection->RemoveById("3");

  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(0, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestSystemNotification) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  scoped_refptr<MockNotificationDelegate> delegate(
      new MockNotificationDelegate("power"));
  NotificationPanelTester* tester = panel->GetTester();

  Notification notify = NewMockNotification(delegate.get());
  collection->AddSystemNotification(notify, browser()->profile(), true, false);

  EXPECT_EQ(1, tester->GetNewNotificationCount());
  EXPECT_EQ(1, tester->GetStickyNotificationCount());

  Notification update = SystemNotificationFactory::Create(
      GURL(), ASCIIToUTF16("Title"), ASCIIToUTF16("updated"), delegate.get());
  collection->UpdateNotification(update);

  EXPECT_EQ(1, tester->GetStickyNotificationCount());

  Notification update_and_show = SystemNotificationFactory::Create(
      GURL(), ASCIIToUTF16("Title"), ASCIIToUTF16("updated and shown"),
      delegate.get());
  collection->UpdateAndShowNotification(update_and_show);

  EXPECT_EQ(1, tester->GetStickyNotificationCount());

  // Dismiss the notification.
  collection->RemoveById(delegate->id());
  ui_test_utils::RunAllPendingInMessageLoop();

  EXPECT_EQ(0, tester->GetStickyNotificationCount());
  EXPECT_EQ(0, tester->GetNewNotificationCount());
  // TODO(oshima): check content, etc..
}

// [CLOSED] -add,add->[STICKY_AND_NEW] -stale-> [MINIMIZED] -remove->
// [MINIMIZED] -remove-> [CLOSED]
IN_PROC_BROWSER_TEST_F(NotificationTest, TestStateTransition1) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();

  tester->SetStaleTimeout(0);
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());

  collection->Add(NewMockNotification("1"), browser()->profile());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  collection->Add(NewMockNotification("2"), browser()->profile());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::MINIMIZED, tester->state());

  collection->RemoveById("2");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::MINIMIZED, tester->state());

  collection->RemoveById("1");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(0, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());

  ui_test_utils::RunAllPendingInMessageLoop();
}

// [CLOSED] -add->[STICKY_AND_NEW] -stale-> [MINIMIZED] -add->
// [STICKY_AND_NEW] -stale-> [MINIMIZED] -add sys-> [STICKY_NEW]
// -stale-> [STICKY_NEW] -remove-> [STICKY_NEW] -remove sys->
// [MINIMIZED] -remove-> [CLOSED]
//
// This test depends on the fact that the panel state change occurs
// quicker than stale timeout, thus the stale timeout cannot be set to
// 0. This test explicitly controls the stale state instead.
IN_PROC_BROWSER_TEST_F(NotificationTest, TestStateTransition2) {
  // Register observer here as the registration does not work in SetUp().
  NotificationRegistrar registrar;
  registrar.Add(this,
                NotificationType::PANEL_STATE_CHANGED,
                NotificationService::AllSources());

  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();

  // See description above.
  tester->SetStaleTimeout(100000);

  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());

  collection->Add(NewMockNotification("1"), browser()->profile());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
  ui_test_utils::RunAllPendingInMessageLoop();

  // Make the notification stale and make sure panel is minimized state.
  MarkStale("1");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::MINIMIZED, tester->state());
  WaitForPanelState(tester, PanelController::MINIMIZED);

  // Adding new notification expands the panel.
  collection->Add(NewMockNotification("2"), browser()->profile());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
  WaitForPanelState(tester, PanelController::EXPANDED);

  // The panel must be minimzied when the new notification becomes stale.
  MarkStale("2");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::MINIMIZED, tester->state());
  WaitForPanelState(tester, PanelController::MINIMIZED);

  // The panel must be expanded again when a new system notification is added.
  collection->AddSystemNotification(
      NewMockNotification("3"), browser()->profile(), true, false);
  EXPECT_EQ(3, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
  WaitForPanelState(tester, PanelController::EXPANDED);

  // Running all events nor removing non sticky should not change the state.
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  collection->RemoveById("1");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  // Removing the system notification should minimize the panel.
  collection->RemoveById("3");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(1, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::MINIMIZED, tester->state());
  WaitForPanelState(tester, PanelController::MINIMIZED);

  // Removing the last notification. Should close the panel.
  collection->RemoveById("2");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(0, tester->GetNotificationCount());
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());

  ui_test_utils::RunAllPendingInMessageLoop();
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestCleanupOnExit) {
  NotificationRegistrar registrar;
  registrar.Add(this,
                NotificationType::PANEL_STATE_CHANGED,
                NotificationService::AllSources());

  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();

  // Don't become stale.
  tester->SetStaleTimeout(100000);

  collection->Add(NewMockNotification("1"), browser()->profile());
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
  WaitForPanelState(tester, PanelController::EXPANDED);
  // end without closing.
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestCloseOpen) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();
  Profile* profile = browser()->profile();

  collection->Add(NewMockNotification("1"), profile);
  collection->Add(NewMockNotification("2"), profile);
  ui_test_utils::RunAllPendingInMessageLoop();
  WaitForPanelState(tester, PanelController::EXPANDED);
  PanelController* controller = tester->GetPanelController();
  // close now
  panel->ClosePanel();
  controller->Close();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());
  // open again
  collection->Add(NewMockNotification("3"), profile);
  WaitForPanelState(tester, PanelController::EXPANDED);
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());

  // close again
  controller = tester->GetPanelController();
  panel->ClosePanel();
  controller->Close();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::CLOSED, tester->state());
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestScrollBalloonToVisible) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();
  Profile* profile = browser()->profile();

  // Create notifications enough to overflow the panel size.
  const int create_count = 15;

  // new notification is always visible
  for (int i = 0; i < create_count; i++) {
    {
      SCOPED_TRACE(base::StringPrintf("new normal %d", i));
      std::string id = base::StringPrintf("n%d", i);
      collection->Add(NewMockNotification(id), profile);
      EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
      BalloonViewImpl* view =
          tester->GetBalloonView(collection, NewMockNotification(id));
      WaitForVisible(view);
    }
    {
      SCOPED_TRACE(base::StringPrintf("new system %d", i));
      std::string id = base::StringPrintf("s%d", i);
      collection->AddSystemNotification(
          NewMockNotification(id), browser()->profile(), true, false);
      BalloonViewImpl* view =
          tester->GetBalloonView(collection, NewMockNotification(id));
      WaitForVisible(view);
    }
  }

  // Update should not change the visibility
  for (int i = 0; i < create_count; i++) {
    {
      SCOPED_TRACE(base::StringPrintf("update n%d", i));
      Notification notify = NewMockNotification(base::StringPrintf("n%d", i));
      // The last shown notification is sticky, which makes all non sticky
      // invisible.
      EXPECT_TRUE(collection->UpdateNotification(notify));
      ui_test_utils::RunAllPendingInMessageLoop();
      BalloonViewImpl* view = tester->GetBalloonView(collection, notify);
      EXPECT_FALSE(tester->IsVisible(view));
    }
    {
      SCOPED_TRACE(base::StringPrintf("update s%d", i));
      Notification notify = NewMockNotification(base::StringPrintf("s%d", i));
      BalloonViewImpl* view = tester->GetBalloonView(collection, notify);
      bool currently_visible = tester->IsVisible(view);
      EXPECT_TRUE(collection->UpdateNotification(notify));
      ui_test_utils::RunAllPendingInMessageLoop();
      EXPECT_EQ(view, tester->GetBalloonView(collection, notify));
      EXPECT_EQ(currently_visible, tester->IsVisible(view));
    }
  }
  // UpdateAndShowNotification makes notification visible
  for (int i = 0; i < create_count; i++) {
    {
      SCOPED_TRACE(base::StringPrintf("update and show n%d", i));
      Notification notify = NewMockNotification(base::StringPrintf("n%d", i));
      EXPECT_TRUE(collection->UpdateAndShowNotification(notify));
      ui_test_utils::RunAllPendingInMessageLoop();
      BalloonViewImpl* view = tester->GetBalloonView(collection, notify);
      EXPECT_TRUE(tester->IsVisible(view));
    }
    {
      SCOPED_TRACE(base::StringPrintf("update and show s%d", i));
      Notification notify = NewMockNotification(base::StringPrintf("s%d", i));
      EXPECT_TRUE(collection->UpdateAndShowNotification(notify));
      ui_test_utils::RunAllPendingInMessageLoop();
      BalloonViewImpl* view = tester->GetBalloonView(collection, notify);
      EXPECT_TRUE(tester->IsVisible(view));
    }
  }
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestActivateDeactivate) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();
  Profile* profile = browser()->profile();

  collection->Add(NewMockNotification("1"), profile);
  collection->AddSystemNotification(
      NewMockNotification("2"), profile, true, false);
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
  BalloonViewImpl* view1 =
      tester->GetBalloonView(collection, NewMockNotification("1"));
  BalloonViewImpl* view2 =
      tester->GetBalloonView(collection, NewMockNotification("2"));
  // Wait until all renderers get size.
  WaitForResize(view1);
  WaitForResize(view2);
  EXPECT_LT(view2->size().height(), 50) << "view size is bigger than expected";

  panel->OnMouseMotion(gfx::Point(10, 50));
  EXPECT_TRUE(tester->IsActive(view1));
  EXPECT_FALSE(tester->IsActive(view2));

  panel->OnMouseMotion(gfx::Point(10, 10));
  EXPECT_FALSE(tester->IsActive(view1));
  EXPECT_TRUE(tester->IsActive(view2));

  panel->OnMouseMotion(gfx::Point(500, 500));
  EXPECT_FALSE(tester->IsActive(view1));
  EXPECT_FALSE(tester->IsActive(view2));
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestCloseDismissAllNonSticky) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  NotificationPanel* panel = GetNotificationPanel();
  NotificationPanelTester* tester = panel->GetTester();
  Profile* profile = browser()->profile();

  collection->Add(NewMockNotification("1"), profile);
  collection->AddSystemNotification(
      NewMockNotification("2"), profile, true, false);
  collection->Add(NewMockNotification("3"), profile);

  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
  EXPECT_EQ(3, tester->GetNotificationCount());
  EXPECT_EQ(1, tester->GetStickyNotificationCount());

  // Hide
  panel->Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ(1, tester->GetNotificationCount());
  EXPECT_EQ(1, tester->GetStickyNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestAddWebUIMessageCallback) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  Profile* profile = browser()->profile();

  collection->AddSystemNotification(
      NewMockNotification("1"), profile, false, false);

  EXPECT_TRUE(collection->AddWebUIMessageCallback(
      NewMockNotification("1"),
      "test",
      NewCallback(
          static_cast<NotificationTest*>(this),
          &NotificationTest::HandleWebUIMessage)));

  // Adding callback for the same message twice should fail.
  EXPECT_FALSE(collection->AddWebUIMessageCallback(
      NewMockNotification("1"),
      "test",
      NewCallback(
          static_cast<NotificationTest*>(this),
          &NotificationTest::HandleWebUIMessage)));

  // Adding callback to nonexistent notification should fail.
  EXPECT_FALSE(collection->AddWebUIMessageCallback(
      NewMockNotification("2"),
      "test1",
      NewCallback(
          static_cast<NotificationTest*>(this),
          &NotificationTest::HandleWebUIMessage)));
}

IN_PROC_BROWSER_TEST_F(NotificationTest, TestWebUIMessageCallback) {
  BalloonCollectionImpl* collection = GetBalloonCollectionImpl();
  Profile* profile = browser()->profile();
  // A notification that sends 'test' WebUI message back to chrome.
  const GURL content_url(
      "data:text/html;charset=utf-8,"
      "<html><script>function send() { chrome.send('test', ['']); }</script>"
      "<body onload='send()'></body></html>");
  collection->AddSystemNotification(
      Notification(GURL(), content_url, string16(), string16(),
                   new MockNotificationDelegate("1")),
      profile,
      false,
      false);
  EXPECT_TRUE(collection->AddWebUIMessageCallback(
      NewMockNotification("1"),
      "test",
      NewCallback(
          static_cast<NotificationTest*>(this),
          &NotificationTest::HandleWebUIMessage)));
  MessageLoop::current()->Run();
}

}  // namespace chromeos