1// Copyright (c) 2011 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/memory/scoped_vector.h"
6#include "chrome/browser/geolocation/geolocation_content_settings_map.h"
7#include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
8#include "chrome/test/testing_profile.h"
9#include "content/browser/browser_thread.h"
10#include "content/browser/geolocation/arbitrator_dependency_factories_for_test.h"
11#include "content/browser/geolocation/geolocation_permission_context.h"
12#include "content/browser/geolocation/location_arbitrator.h"
13#include "content/browser/geolocation/location_provider.h"
14#include "content/browser/geolocation/mock_location_provider.h"
15#include "content/browser/renderer_host/mock_render_process_host.h"
16#include "content/browser/renderer_host/test_render_view_host.h"
17#include "content/browser/tab_contents/test_tab_contents.h"
18#include "content/common/geolocation_messages.h"
19#include "content/common/notification_details.h"
20#include "content/common/notification_type.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23// TestTabContentsWithPendingInfoBar ------------------------------------------
24
25namespace {
26
27// TestTabContents short-circuits TAB_CONTENTS_INFOBAR_REMOVED to call
28// InfoBarClosed() directly. We need to observe it and call InfoBarClosed()
29// later.
30class TestTabContentsWithPendingInfoBar : public TestTabContents {
31 public:
32  TestTabContentsWithPendingInfoBar(Profile* profile, SiteInstance* instance);
33  virtual ~TestTabContentsWithPendingInfoBar();
34
35  // TestTabContents:
36  virtual void Observe(NotificationType type,
37                       const NotificationSource& source,
38                       const NotificationDetails& details);
39
40  InfoBarDelegate* removed_infobar_delegate_;
41};
42
43TestTabContentsWithPendingInfoBar::TestTabContentsWithPendingInfoBar(
44    Profile* profile,
45    SiteInstance* instance)
46    : TestTabContents(profile, instance),
47      removed_infobar_delegate_(NULL) {
48}
49
50TestTabContentsWithPendingInfoBar::~TestTabContentsWithPendingInfoBar() {
51}
52
53void TestTabContentsWithPendingInfoBar::Observe(
54    NotificationType type,
55    const NotificationSource& source,
56    const NotificationDetails& details) {
57  if (type.value == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED)
58    removed_infobar_delegate_ = Details<InfoBarDelegate>(details).ptr();
59  else
60    TestTabContents::Observe(type, source, details);
61}
62
63}  // namespace
64
65
66// GeolocationPermissionContextTests ------------------------------------------
67
68// This class sets up GeolocationArbitrator.
69class GeolocationPermissionContextTests : public RenderViewHostTestHarness {
70 public:
71  GeolocationPermissionContextTests();
72
73 protected:
74  virtual ~GeolocationPermissionContextTests();
75
76  int process_id() { return contents()->render_view_host()->process()->id(); }
77  int process_id_for_tab(int tab) {
78    return extra_tabs_[tab]->render_view_host()->process()->id();
79  }
80  int render_id() { return contents()->render_view_host()->routing_id(); }
81  int render_id_for_tab(int tab) {
82    return extra_tabs_[tab]->render_view_host()->routing_id();
83  }
84  int bridge_id() const { return 42; }  // Not relevant at this level.
85
86  void CheckPermissionMessageSent(int bridge_id, bool allowed);
87  void CheckPermissionMessageSentForTab(int tab, int bridge_id, bool allowed);
88  void CheckPermissionMessageSentInternal(MockRenderProcessHost* process,
89                                          int bridge_id,
90                                          bool allowed);
91  void AddNewTab(const GURL& url);
92  void CheckTabContentsState(const GURL& requesting_frame,
93                             ContentSetting expected_content_setting);
94
95  TestTabContentsWithPendingInfoBar* tab_contents_with_pending_infobar_;
96  scoped_refptr<GeolocationPermissionContext> geolocation_permission_context_;
97  ScopedVector<TestTabContentsWithPendingInfoBar> extra_tabs_;
98
99 private:
100  // RenderViewHostTestHarness:
101  virtual void SetUp();
102  virtual void TearDown();
103
104  BrowserThread ui_thread_;
105  scoped_refptr<GeolocationArbitratorDependencyFactory> dependency_factory_;
106};
107
108GeolocationPermissionContextTests::GeolocationPermissionContextTests()
109    : RenderViewHostTestHarness(),
110      tab_contents_with_pending_infobar_(NULL),
111      ui_thread_(BrowserThread::UI, MessageLoop::current()),
112      dependency_factory_(
113          new GeolocationArbitratorDependencyFactoryWithLocationProvider(
114              &NewAutoSuccessMockNetworkLocationProvider)) {
115}
116
117GeolocationPermissionContextTests::~GeolocationPermissionContextTests() {
118}
119
120void GeolocationPermissionContextTests::CheckPermissionMessageSent(
121    int bridge_id,
122    bool allowed) {
123  CheckPermissionMessageSentInternal(process(), bridge_id, allowed);
124}
125
126void GeolocationPermissionContextTests::CheckPermissionMessageSentForTab(
127    int tab,
128    int bridge_id,
129    bool allowed) {
130  CheckPermissionMessageSentInternal(static_cast<MockRenderProcessHost*>(
131      extra_tabs_[tab]->render_view_host()->process()), bridge_id, allowed);
132}
133
134void GeolocationPermissionContextTests::CheckPermissionMessageSentInternal(
135    MockRenderProcessHost* process,
136    int bridge_id,
137    bool allowed) {
138  MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
139  MessageLoop::current()->Run();
140  const IPC::Message* message = process->sink().GetFirstMessageMatching(
141      GeolocationMsg_PermissionSet::ID);
142  ASSERT_TRUE(message);
143  GeolocationMsg_PermissionSet::Param param;
144  GeolocationMsg_PermissionSet::Read(message, &param);
145  EXPECT_EQ(bridge_id, param.a);
146  EXPECT_EQ(allowed, param.b);
147  process->sink().ClearMessages();
148}
149
150void GeolocationPermissionContextTests::AddNewTab(const GURL& url) {
151  TestTabContentsWithPendingInfoBar* new_tab =
152      new TestTabContentsWithPendingInfoBar(profile(), NULL);
153  new_tab->controller().LoadURL(url, GURL(), PageTransition::TYPED);
154  static_cast<TestRenderViewHost*>(new_tab->render_manager()->current_host())->
155      SendNavigate(extra_tabs_.size() + 1, url);
156  extra_tabs_.push_back(new_tab);
157}
158
159void GeolocationPermissionContextTests::CheckTabContentsState(
160    const GURL& requesting_frame,
161    ContentSetting expected_content_setting) {
162  TabSpecificContentSettings* content_settings =
163      contents()->GetTabSpecificContentSettings();
164  const GeolocationSettingsState::StateMap& state_map =
165      content_settings->geolocation_settings_state().state_map();
166  EXPECT_EQ(1U, state_map.count(requesting_frame.GetOrigin()));
167  EXPECT_EQ(0U, state_map.count(requesting_frame));
168  GeolocationSettingsState::StateMap::const_iterator settings =
169      state_map.find(requesting_frame.GetOrigin());
170  ASSERT_FALSE(settings == state_map.end())
171      << "geolocation state not found " << requesting_frame;
172  EXPECT_EQ(expected_content_setting, settings->second);
173}
174
175void GeolocationPermissionContextTests::SetUp() {
176  RenderViewHostTestHarness::SetUp();
177  GeolocationArbitrator::SetDependencyFactoryForTest(
178      dependency_factory_.get());
179  SiteInstance* site_instance = contents()->GetSiteInstance();
180  tab_contents_with_pending_infobar_ =
181      new TestTabContentsWithPendingInfoBar(profile_.get(), site_instance);
182  SetContents(tab_contents_with_pending_infobar_);
183  geolocation_permission_context_ =
184      new GeolocationPermissionContext(profile());
185}
186
187void GeolocationPermissionContextTests::TearDown() {
188  GeolocationArbitrator::SetDependencyFactoryForTest(NULL);
189  RenderViewHostTestHarness::TearDown();
190}
191
192
193// Tests ----------------------------------------------------------------------
194
195TEST_F(GeolocationPermissionContextTests, SinglePermission) {
196  GURL requesting_frame("http://www.example.com/geolocation");
197  NavigateAndCommit(requesting_frame);
198  EXPECT_EQ(0U, contents()->infobar_count());
199  geolocation_permission_context_->RequestGeolocationPermission(
200      process_id(), render_id(), bridge_id(), requesting_frame);
201  EXPECT_EQ(1U, contents()->infobar_count());
202}
203
204TEST_F(GeolocationPermissionContextTests, QueuedPermission) {
205  GURL requesting_frame_0("http://www.example.com/geolocation");
206  GURL requesting_frame_1("http://www.example-2.com/geolocation");
207  EXPECT_EQ(CONTENT_SETTING_ASK,
208      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
209          requesting_frame_0, requesting_frame_0));
210  EXPECT_EQ(CONTENT_SETTING_ASK,
211      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
212          requesting_frame_1, requesting_frame_0));
213
214  NavigateAndCommit(requesting_frame_0);
215  EXPECT_EQ(0U, contents()->infobar_count());
216  // Request permission for two frames.
217  geolocation_permission_context_->RequestGeolocationPermission(
218      process_id(), render_id(), bridge_id(), requesting_frame_0);
219  geolocation_permission_context_->RequestGeolocationPermission(
220      process_id(), render_id(), bridge_id() + 1, requesting_frame_1);
221  // Ensure only one infobar is created.
222  EXPECT_EQ(1U, contents()->infobar_count());
223  ConfirmInfoBarDelegate* infobar_0 =
224      contents()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
225  ASSERT_TRUE(infobar_0);
226  string16 text_0 = infobar_0->GetMessageText();
227
228  // Accept the first frame.
229  infobar_0->Accept();
230  CheckTabContentsState(requesting_frame_0, CONTENT_SETTING_ALLOW);
231  CheckPermissionMessageSent(bridge_id(), true);
232
233  contents()->RemoveInfoBar(infobar_0);
234  EXPECT_EQ(infobar_0,
235            tab_contents_with_pending_infobar_->removed_infobar_delegate_);
236  infobar_0->InfoBarClosed();
237  // Now we should have a new infobar for the second frame.
238  EXPECT_EQ(1U, contents()->infobar_count());
239
240  ConfirmInfoBarDelegate* infobar_1 =
241      contents()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
242  ASSERT_TRUE(infobar_1);
243  string16 text_1 = infobar_1->GetMessageText();
244  EXPECT_NE(text_0, text_1);
245
246  // Cancel (block) this frame.
247  infobar_1->Cancel();
248  CheckTabContentsState(requesting_frame_1, CONTENT_SETTING_BLOCK);
249  CheckPermissionMessageSent(bridge_id() + 1, false);
250  contents()->RemoveInfoBar(infobar_1);
251  EXPECT_EQ(infobar_1,
252            tab_contents_with_pending_infobar_->removed_infobar_delegate_);
253  infobar_1->InfoBarClosed();
254  EXPECT_EQ(0U, contents()->infobar_count());
255  // Ensure the persisted permissions are ok.
256  EXPECT_EQ(CONTENT_SETTING_ALLOW,
257      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
258          requesting_frame_0, requesting_frame_0));
259  EXPECT_EQ(CONTENT_SETTING_BLOCK,
260      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
261          requesting_frame_1, requesting_frame_0));
262}
263
264TEST_F(GeolocationPermissionContextTests, CancelGeolocationPermissionRequest) {
265  GURL requesting_frame_0("http://www.example.com/geolocation");
266  GURL requesting_frame_1("http://www.example-2.com/geolocation");
267  EXPECT_EQ(CONTENT_SETTING_ASK,
268      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
269          requesting_frame_0, requesting_frame_0));
270  EXPECT_EQ(CONTENT_SETTING_ASK,
271      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
272          requesting_frame_1, requesting_frame_0));
273
274  NavigateAndCommit(requesting_frame_0);
275  EXPECT_EQ(0U, contents()->infobar_count());
276  // Request permission for two frames.
277  geolocation_permission_context_->RequestGeolocationPermission(
278      process_id(), render_id(), bridge_id(), requesting_frame_0);
279  geolocation_permission_context_->RequestGeolocationPermission(
280      process_id(), render_id(), bridge_id() + 1, requesting_frame_1);
281  EXPECT_EQ(1U, contents()->infobar_count());
282
283  ConfirmInfoBarDelegate* infobar_0 =
284      contents()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
285  ASSERT_TRUE(infobar_0);
286  string16 text_0 = infobar_0->GetMessageText();
287
288  // Simulate the frame going away, ensure the infobar for this frame
289  // is removed and the next pending infobar is created.
290  geolocation_permission_context_->CancelGeolocationPermissionRequest(
291      process_id(), render_id(), bridge_id(), requesting_frame_0);
292  EXPECT_EQ(infobar_0,
293            tab_contents_with_pending_infobar_->removed_infobar_delegate_);
294  infobar_0->InfoBarClosed();
295  EXPECT_EQ(1U, contents()->infobar_count());
296
297  ConfirmInfoBarDelegate* infobar_1 =
298      contents()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
299  ASSERT_TRUE(infobar_1);
300  string16 text_1 = infobar_1->GetMessageText();
301  EXPECT_NE(text_0, text_1);
302
303  // Allow this frame.
304  infobar_1->Accept();
305  CheckTabContentsState(requesting_frame_1, CONTENT_SETTING_ALLOW);
306  CheckPermissionMessageSent(bridge_id() + 1, true);
307  contents()->RemoveInfoBar(infobar_1);
308  EXPECT_EQ(infobar_1,
309            tab_contents_with_pending_infobar_->removed_infobar_delegate_);
310  infobar_1->InfoBarClosed();
311  EXPECT_EQ(0U, contents()->infobar_count());
312  // Ensure the persisted permissions are ok.
313  EXPECT_EQ(CONTENT_SETTING_ASK,
314      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
315          requesting_frame_0, requesting_frame_0));
316  EXPECT_EQ(CONTENT_SETTING_ALLOW,
317      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
318          requesting_frame_1, requesting_frame_0));
319}
320
321TEST_F(GeolocationPermissionContextTests, InvalidURL) {
322  GURL invalid_embedder;
323  GURL requesting_frame("about:blank");
324  NavigateAndCommit(invalid_embedder);
325  EXPECT_EQ(0U, contents()->infobar_count());
326  geolocation_permission_context_->RequestGeolocationPermission(
327      process_id(), render_id(), bridge_id(), requesting_frame);
328  EXPECT_EQ(0U, contents()->infobar_count());
329  CheckPermissionMessageSent(bridge_id(), false);
330}
331
332TEST_F(GeolocationPermissionContextTests, SameOriginMultipleTabs) {
333  GURL url_a("http://www.example.com/geolocation");
334  GURL url_b("http://www.example-2.com/geolocation");
335  NavigateAndCommit(url_a);
336  AddNewTab(url_b);
337  AddNewTab(url_a);
338
339  EXPECT_EQ(0U, contents()->infobar_count());
340  geolocation_permission_context_->RequestGeolocationPermission(
341      process_id(), render_id(), bridge_id(), url_a);
342  EXPECT_EQ(1U, contents()->infobar_count());
343
344  geolocation_permission_context_->RequestGeolocationPermission(
345      process_id_for_tab(0), render_id_for_tab(0), bridge_id(), url_b);
346  EXPECT_EQ(1U, extra_tabs_[0]->infobar_count());
347
348  geolocation_permission_context_->RequestGeolocationPermission(
349      process_id_for_tab(1), render_id_for_tab(1), bridge_id(), url_a);
350  EXPECT_EQ(1U, extra_tabs_[1]->infobar_count());
351
352  ConfirmInfoBarDelegate* removed_infobar =
353      extra_tabs_[1]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
354
355  // Accept the first tab.
356  ConfirmInfoBarDelegate* infobar_0 =
357      contents()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
358  ASSERT_TRUE(infobar_0);
359  infobar_0->Accept();
360  CheckPermissionMessageSent(bridge_id(), true);
361  contents()->RemoveInfoBar(infobar_0);
362  EXPECT_EQ(infobar_0,
363            tab_contents_with_pending_infobar_->removed_infobar_delegate_);
364  infobar_0->InfoBarClosed();
365  // Now the infobar for the tab with the same origin should have gone.
366  EXPECT_EQ(0U, extra_tabs_[1]->infobar_count());
367  CheckPermissionMessageSentForTab(1, bridge_id(), true);
368  // Destroy the infobar that has just been removed.
369  removed_infobar->InfoBarClosed();
370
371  // But the other tab should still have the info bar...
372  EXPECT_EQ(1U, extra_tabs_[0]->infobar_count());
373  extra_tabs_.reset();
374}
375
376TEST_F(GeolocationPermissionContextTests, QueuedOriginMultipleTabs) {
377  GURL url_a("http://www.example.com/geolocation");
378  GURL url_b("http://www.example-2.com/geolocation");
379  NavigateAndCommit(url_a);
380  AddNewTab(url_a);
381
382  EXPECT_EQ(0U, contents()->infobar_count());
383  geolocation_permission_context_->RequestGeolocationPermission(
384      process_id(), render_id(), bridge_id(), url_a);
385  EXPECT_EQ(1U, contents()->infobar_count());
386
387  geolocation_permission_context_->RequestGeolocationPermission(
388      process_id_for_tab(0), render_id_for_tab(0), bridge_id(), url_a);
389  EXPECT_EQ(1U, extra_tabs_[0]->infobar_count());
390
391  geolocation_permission_context_->RequestGeolocationPermission(
392      process_id_for_tab(0), render_id_for_tab(0), bridge_id() + 1, url_b);
393  EXPECT_EQ(1U, extra_tabs_[0]->infobar_count());
394
395  ConfirmInfoBarDelegate* removed_infobar =
396      contents()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
397
398  // Accept the second tab.
399  ConfirmInfoBarDelegate* infobar_0 =
400      extra_tabs_[0]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
401  ASSERT_TRUE(infobar_0);
402  infobar_0->Accept();
403  CheckPermissionMessageSentForTab(0, bridge_id(), true);
404  extra_tabs_[0]->RemoveInfoBar(infobar_0);
405  EXPECT_EQ(infobar_0,
406            extra_tabs_[0]->removed_infobar_delegate_);
407  infobar_0->InfoBarClosed();
408  // Now the infobar for the tab with the same origin should have gone.
409  EXPECT_EQ(0U, contents()->infobar_count());
410  CheckPermissionMessageSent(bridge_id(), true);
411  // Destroy the infobar that has just been removed.
412  removed_infobar->InfoBarClosed();
413
414  // And we should have the queued infobar displayed now.
415  EXPECT_EQ(1U, extra_tabs_[0]->infobar_count());
416
417  // Accept the second infobar.
418  ConfirmInfoBarDelegate* infobar_1 =
419      extra_tabs_[0]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
420  ASSERT_TRUE(infobar_1);
421  infobar_1->Accept();
422  CheckPermissionMessageSentForTab(0, bridge_id() + 1, true);
423  extra_tabs_[0]->RemoveInfoBar(infobar_1);
424  EXPECT_EQ(infobar_1,
425            extra_tabs_[0]->removed_infobar_delegate_);
426  infobar_1->InfoBarClosed();
427
428  extra_tabs_.reset();
429}
430
431TEST_F(GeolocationPermissionContextTests, TabDestroyed) {
432  GURL requesting_frame_0("http://www.example.com/geolocation");
433  GURL requesting_frame_1("http://www.example-2.com/geolocation");
434  EXPECT_EQ(
435      CONTENT_SETTING_ASK,
436      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
437          requesting_frame_0, requesting_frame_0));
438  EXPECT_EQ(
439      CONTENT_SETTING_ASK,
440      profile()->GetGeolocationContentSettingsMap()->GetContentSetting(
441          requesting_frame_1, requesting_frame_0));
442
443  NavigateAndCommit(requesting_frame_0);
444  EXPECT_EQ(0U, contents()->infobar_count());
445  // Request permission for two frames.
446  geolocation_permission_context_->RequestGeolocationPermission(
447      process_id(), render_id(), bridge_id(), requesting_frame_0);
448  geolocation_permission_context_->RequestGeolocationPermission(
449      process_id(), render_id(), bridge_id() + 1, requesting_frame_1);
450  // Ensure only one infobar is created.
451  EXPECT_EQ(1U, contents()->infobar_count());
452  ConfirmInfoBarDelegate* infobar_0 =
453      contents()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate();
454  ASSERT_TRUE(infobar_0);
455  string16 text_0 = infobar_0->GetMessageText();
456
457  // Delete the tab contents.
458  DeleteContents();
459}
460