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.
4
5#include "base/strings/string_number_conversions.h"
6#include "base/strings/stringprintf.h"
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/browser_process.h"
9#include "chrome/browser/extensions/api/notifications/notifications_api.h"
10#include "chrome/browser/extensions/extension_apitest.h"
11#include "chrome/browser/extensions/extension_function_test_utils.h"
12#include "chrome/browser/notifications/notification.h"
13#include "chrome/browser/notifications/notification_ui_manager.h"
14#include "content/public/browser/notification_service.h"
15#include "content/public/test/test_utils.h"
16#include "extensions/browser/api/test/test_api.h"
17#include "extensions/browser/notification_types.h"
18#include "extensions/common/features/feature.h"
19#include "extensions/common/test_util.h"
20#include "extensions/test/result_catcher.h"
21#include "ui/message_center/message_center.h"
22#include "ui/message_center/notification_list.h"
23#include "ui/message_center/notifier_settings.h"
24
25using extensions::Extension;
26using extensions::ResultCatcher;
27
28namespace utils = extension_function_test_utils;
29
30namespace {
31
32// A class that waits for a |chrome.test.sendMessage| call, ignores the message,
33// and writes down the user gesture status of the message.
34class UserGestureCatcher : public content::NotificationObserver {
35 public:
36  UserGestureCatcher() : waiting_(false) {
37    registrar_.Add(this,
38                   extensions::NOTIFICATION_EXTENSION_TEST_MESSAGE,
39                   content::NotificationService::AllSources());
40  }
41
42  virtual ~UserGestureCatcher() {}
43
44  bool GetNextResult() {
45    if (results_.empty()) {
46      waiting_ = true;
47      content::RunMessageLoop();
48      waiting_ = false;
49    }
50
51    if (!results_.empty()) {
52      bool ret = results_.front();
53      results_.pop_front();
54      return ret;
55    }
56    NOTREACHED();
57    return false;
58  }
59
60 private:
61  virtual void Observe(int type,
62                       const content::NotificationSource& source,
63                       const content::NotificationDetails& details) OVERRIDE {
64    results_.push_back(
65        static_cast<content::Source<extensions::TestSendMessageFunction> >(
66            source)
67            .ptr()
68            ->user_gesture());
69    if (waiting_)
70      base::MessageLoopForUI::current()->Quit();
71  }
72
73  content::NotificationRegistrar registrar_;
74
75  // A sequential list of user gesture notifications from the test extension(s).
76  std::deque<bool> results_;
77
78  // True if we're in a nested message loop waiting for results from
79  // the extension.
80  bool waiting_;
81};
82
83class NotificationsApiTest : public ExtensionApiTest {
84 public:
85  const extensions::Extension* LoadExtensionAndWait(
86      const std::string& test_name) {
87    base::FilePath extdir = test_data_dir_.AppendASCII(test_name);
88    content::WindowedNotificationObserver page_created(
89        extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
90        content::NotificationService::AllSources());
91    const extensions::Extension* extension = LoadExtension(extdir);
92    if (extension) {
93      page_created.Wait();
94    }
95    return extension;
96  }
97};
98
99}  // namespace
100
101IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestBasicUsage) {
102  ASSERT_TRUE(RunExtensionTest("notifications/api/basic_usage")) << message_;
103}
104
105IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestEvents) {
106  ASSERT_TRUE(RunExtensionTest("notifications/api/events")) << message_;
107}
108
109IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestCSP) {
110  ASSERT_TRUE(RunExtensionTest("notifications/api/csp")) << message_;
111}
112
113IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestByUser) {
114  const extensions::Extension* extension =
115      LoadExtensionAndWait("notifications/api/by_user");
116  ASSERT_TRUE(extension) << message_;
117
118  {
119    ResultCatcher catcher;
120    g_browser_process->message_center()->RemoveNotification(
121        extension->id() + "-FOO",
122        false);
123    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
124  }
125
126  {
127    ResultCatcher catcher;
128    g_browser_process->message_center()->RemoveNotification(
129        extension->id() + "-BAR",
130        true);
131    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
132  }
133
134  {
135    ResultCatcher catcher;
136    g_browser_process->message_center()->RemoveAllNotifications(false);
137    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
138  }
139  {
140    ResultCatcher catcher;
141    g_browser_process->message_center()->RemoveAllNotifications(true);
142    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
143  }
144}
145
146IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestPartialUpdate) {
147  ASSERT_TRUE(RunExtensionTest("notifications/api/partial_update")) << message_;
148  const extensions::Extension* extension = GetSingleLoadedExtension();
149  ASSERT_TRUE(extension) << message_;
150
151  const char kNewTitle[] = "Changed!";
152  const char kNewMessage[] = "Too late! The show ended yesterday";
153  int kNewPriority = 2;
154
155  const message_center::NotificationList::Notifications& notifications =
156      g_browser_process->message_center()->GetVisibleNotifications();
157  ASSERT_EQ(1u, notifications.size());
158  message_center::Notification* notification = *(notifications.begin());
159  LOG(INFO) << "Notification ID: " << notification->id();
160
161  EXPECT_EQ(base::ASCIIToUTF16(kNewTitle), notification->title());
162  EXPECT_EQ(base::ASCIIToUTF16(kNewMessage), notification->message());
163  EXPECT_EQ(kNewPriority, notification->priority());
164  EXPECT_EQ(0u, notification->buttons().size());
165}
166
167IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestGetPermissionLevel) {
168  scoped_refptr<Extension> empty_extension(
169      extensions::test_util::CreateEmptyExtension());
170
171  // Get permission level for the extension whose notifications are enabled.
172  {
173    scoped_refptr<extensions::NotificationsGetPermissionLevelFunction>
174        notification_function(
175            new extensions::NotificationsGetPermissionLevelFunction());
176
177    notification_function->set_extension(empty_extension.get());
178    notification_function->set_has_callback(true);
179
180    scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
181        notification_function.get(),
182        "[]",
183        browser(),
184        utils::NONE));
185
186    EXPECT_EQ(base::Value::TYPE_STRING, result->GetType());
187    std::string permission_level;
188    EXPECT_TRUE(result->GetAsString(&permission_level));
189    EXPECT_EQ("granted", permission_level);
190  }
191
192  // Get permission level for the extension whose notifications are disabled.
193  {
194    scoped_refptr<extensions::NotificationsGetPermissionLevelFunction>
195        notification_function(
196            new extensions::NotificationsGetPermissionLevelFunction());
197
198    notification_function->set_extension(empty_extension.get());
199    notification_function->set_has_callback(true);
200
201    message_center::NotifierId notifier_id(
202        message_center::NotifierId::APPLICATION,
203        empty_extension->id());
204    message_center::Notifier notifier(notifier_id, base::string16(), true);
205    g_browser_process->message_center()->GetNotifierSettingsProvider()->
206        SetNotifierEnabled(notifier, false);
207
208    scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
209        notification_function.get(),
210        "[]",
211        browser(),
212        utils::NONE));
213
214    EXPECT_EQ(base::Value::TYPE_STRING, result->GetType());
215    std::string permission_level;
216    EXPECT_TRUE(result->GetAsString(&permission_level));
217    EXPECT_EQ("denied", permission_level);
218  }
219}
220
221IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestOnPermissionLevelChanged) {
222  const extensions::Extension* extension =
223      LoadExtensionAndWait("notifications/api/permission");
224  ASSERT_TRUE(extension) << message_;
225
226  // Test permission level changing from granted to denied.
227  {
228    ResultCatcher catcher;
229
230    message_center::NotifierId notifier_id(
231        message_center::NotifierId::APPLICATION,
232        extension->id());
233    message_center::Notifier notifier(notifier_id, base::string16(), true);
234    g_browser_process->message_center()->GetNotifierSettingsProvider()->
235        SetNotifierEnabled(notifier, false);
236
237    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
238  }
239
240  // Test permission level changing from denied to granted.
241  {
242    ResultCatcher catcher;
243
244    message_center::NotifierId notifier_id(
245        message_center::NotifierId::APPLICATION,
246        extension->id());
247    message_center::Notifier notifier(notifier_id, base::string16(), false);
248    g_browser_process->message_center()->GetNotifierSettingsProvider()->
249        SetNotifierEnabled(notifier, true);
250
251    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
252  }
253}
254
255IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestUserGesture) {
256  const extensions::Extension* extension =
257      LoadExtensionAndWait("notifications/api/user_gesture");
258  ASSERT_TRUE(extension) << message_;
259
260  const message_center::NotificationList::Notifications& notifications =
261      g_browser_process->message_center()->GetVisibleNotifications();
262  ASSERT_EQ(1u, notifications.size());
263  message_center::Notification* notification = *(notifications.begin());
264
265  {
266    UserGestureCatcher catcher;
267    notification->ButtonClick(0);
268    EXPECT_TRUE(catcher.GetNextResult());
269    notification->Click();
270    EXPECT_TRUE(catcher.GetNextResult());
271    notification->Close(true);
272    EXPECT_TRUE(catcher.GetNextResult());
273    notification->Close(false);
274    EXPECT_FALSE(catcher.GetNextResult());
275  }
276}
277