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 <string>
6
7#include "base/compiler_specific.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/stringprintf.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/test/simple_test_clock.h"
12#include "base/time/clock.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/content_settings/content_settings_usages_state.h"
15#include "chrome/browser/content_settings/host_content_settings_map.h"
16#include "chrome/browser/content_settings/tab_specific_content_settings.h"
17#include "chrome/browser/infobars/infobar_service.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/browser/ui/browser.h"
20#include "chrome/browser/ui/browser_commands.h"
21#include "chrome/browser/ui/tabs/tab_strip_model.h"
22#include "chrome/common/chrome_paths.h"
23#include "chrome/test/base/in_process_browser_test.h"
24#include "chrome/test/base/ui_test_utils.h"
25#include "components/content_settings/core/common/content_settings_pattern.h"
26#include "components/infobars/core/confirm_infobar_delegate.h"
27#include "components/infobars/core/infobar.h"
28#include "content/public/browser/dom_operation_notification_details.h"
29#include "content/public/browser/navigation_controller.h"
30#include "content/public/browser/notification_details.h"
31#include "content/public/browser/notification_service.h"
32#include "content/public/browser/render_frame_host.h"
33#include "content/public/browser/web_contents.h"
34#include "content/public/test/browser_test_utils.h"
35#include "net/base/net_util.h"
36#include "net/test/embedded_test_server/embedded_test_server.h"
37
38using content::DomOperationNotificationDetails;
39using content::NavigationController;
40using content::WebContents;
41
42namespace {
43
44// IFrameLoader ---------------------------------------------------------------
45
46// Used to block until an iframe is loaded via a javascript call.
47// Note: NavigateToURLBlockUntilNavigationsComplete doesn't seem to work for
48// multiple embedded iframes, as notifications seem to be 'batched'. Instead, we
49// load and wait one single frame here by calling a javascript function.
50class IFrameLoader : public content::NotificationObserver {
51 public:
52  IFrameLoader(Browser* browser, int iframe_id, const GURL& url);
53  virtual ~IFrameLoader();
54
55  // content::NotificationObserver:
56  virtual void Observe(int type,
57                       const content::NotificationSource& source,
58                       const content::NotificationDetails& details) OVERRIDE;
59
60  const GURL& iframe_url() const { return iframe_url_; }
61
62 private:
63  content::NotificationRegistrar registrar_;
64
65  // If true the navigation has completed.
66  bool navigation_completed_;
67
68  // If true the javascript call has completed.
69  bool javascript_completed_;
70
71  std::string javascript_response_;
72
73  // The URL for the iframe we just loaded.
74  GURL iframe_url_;
75
76  DISALLOW_COPY_AND_ASSIGN(IFrameLoader);
77};
78
79IFrameLoader::IFrameLoader(Browser* browser, int iframe_id, const GURL& url)
80    : navigation_completed_(false),
81      javascript_completed_(false) {
82  WebContents* web_contents =
83      browser->tab_strip_model()->GetActiveWebContents();
84  NavigationController* controller = &web_contents->GetController();
85  registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
86                 content::Source<NavigationController>(controller));
87  registrar_.Add(this, content::NOTIFICATION_DOM_OPERATION_RESPONSE,
88                 content::NotificationService::AllSources());
89  std::string script(base::StringPrintf(
90      "window.domAutomationController.setAutomationId(0);"
91      "window.domAutomationController.send(addIFrame(%d, \"%s\"));",
92      iframe_id, url.spec().c_str()));
93  web_contents->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(script));
94  content::RunMessageLoop();
95
96  EXPECT_EQ(base::StringPrintf("\"%d\"", iframe_id), javascript_response_);
97  registrar_.RemoveAll();
98  // Now that we loaded the iframe, let's fetch its src.
99  script = base::StringPrintf(
100      "window.domAutomationController.send(getIFrameSrc(%d))", iframe_id);
101  std::string iframe_src;
102  EXPECT_TRUE(content::ExecuteScriptAndExtractString(web_contents, script,
103                                                     &iframe_src));
104  iframe_url_ = GURL(iframe_src);
105}
106
107IFrameLoader::~IFrameLoader() {
108}
109
110void IFrameLoader::Observe(int type,
111                           const content::NotificationSource& source,
112                           const content::NotificationDetails& details) {
113  if (type == content::NOTIFICATION_LOAD_STOP) {
114    navigation_completed_ = true;
115  } else if (type == content::NOTIFICATION_DOM_OPERATION_RESPONSE) {
116    content::Details<DomOperationNotificationDetails> dom_op_details(details);
117    javascript_response_ = dom_op_details->json;
118    javascript_completed_ = true;
119  }
120  if (javascript_completed_ && navigation_completed_)
121    base::MessageLoopForUI::current()->Quit();
122}
123
124// GeolocationNotificationObserver --------------------------------------------
125
126class GeolocationNotificationObserver : public content::NotificationObserver {
127 public:
128  // If |wait_for_infobar| is true, AddWatchAndWaitForNotification will block
129  // until the infobar has been displayed; otherwise it will block until the
130  // navigation is completed.
131  explicit GeolocationNotificationObserver(bool wait_for_infobar);
132  virtual ~GeolocationNotificationObserver();
133
134  // content::NotificationObserver:
135  virtual void Observe(int type,
136                       const content::NotificationSource& source,
137                       const content::NotificationDetails& details) OVERRIDE;
138
139  void AddWatchAndWaitForNotification(
140      content::RenderFrameHost* render_frame_host);
141
142  bool has_infobar() const { return !!infobar_; }
143  infobars::InfoBar* infobar() { return infobar_; }
144
145 private:
146  content::NotificationRegistrar registrar_;
147  bool wait_for_infobar_;
148  infobars::InfoBar* infobar_;
149  bool navigation_started_;
150  bool navigation_completed_;
151  std::string javascript_response_;
152
153  DISALLOW_COPY_AND_ASSIGN(GeolocationNotificationObserver);
154};
155
156GeolocationNotificationObserver::GeolocationNotificationObserver(
157    bool wait_for_infobar)
158    : wait_for_infobar_(wait_for_infobar),
159      infobar_(NULL),
160      navigation_started_(false),
161      navigation_completed_(false) {
162  registrar_.Add(this, content::NOTIFICATION_DOM_OPERATION_RESPONSE,
163                 content::NotificationService::AllSources());
164  if (wait_for_infobar) {
165    registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
166                   content::NotificationService::AllSources());
167  } else {
168    registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
169                   content::NotificationService::AllSources());
170    registrar_.Add(this, content::NOTIFICATION_LOAD_START,
171                   content::NotificationService::AllSources());
172    registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
173                   content::NotificationService::AllSources());
174  }
175}
176
177GeolocationNotificationObserver::~GeolocationNotificationObserver() {
178}
179
180void GeolocationNotificationObserver::Observe(
181    int type,
182    const content::NotificationSource& source,
183    const content::NotificationDetails& details) {
184  if (type == chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED) {
185    infobar_ = content::Details<infobars::InfoBar::AddedDetails>(details).ptr();
186    ASSERT_FALSE(infobar_->delegate()->GetIcon().IsEmpty());
187    ASSERT_TRUE(infobar_->delegate()->AsConfirmInfoBarDelegate());
188  } else if (type == content::NOTIFICATION_DOM_OPERATION_RESPONSE) {
189    content::Details<DomOperationNotificationDetails> dom_op_details(details);
190    javascript_response_ = dom_op_details->json;
191    LOG(WARNING) << "javascript_response " << javascript_response_;
192  } else if ((type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) ||
193             (type == content::NOTIFICATION_LOAD_START)) {
194    navigation_started_ = true;
195  } else if ((type == content::NOTIFICATION_LOAD_STOP) && navigation_started_) {
196    navigation_started_ = false;
197    navigation_completed_ = true;
198  }
199
200  // We're either waiting for just the infobar, or for both a javascript
201  // prompt and response.
202  if ((wait_for_infobar_ && infobar_) ||
203      (navigation_completed_ && !javascript_response_.empty()))
204    base::MessageLoopForUI::current()->Quit();
205}
206
207void GeolocationNotificationObserver::AddWatchAndWaitForNotification(
208    content::RenderFrameHost* render_frame_host) {
209  LOG(WARNING) << "will add geolocation watch";
210  std::string script(
211      "window.domAutomationController.setAutomationId(0);"
212      "window.domAutomationController.send(geoStart());");
213  render_frame_host->ExecuteJavaScript(base::UTF8ToUTF16(script));
214  content::RunMessageLoop();
215  registrar_.RemoveAll();
216  LOG(WARNING) << "got geolocation watch" << javascript_response_;
217  EXPECT_NE("\"0\"", javascript_response_);
218  EXPECT_TRUE(wait_for_infobar_ ? (infobar_ != NULL) : navigation_completed_);
219}
220
221}  // namespace
222
223
224// GeolocationBrowserTest -----------------------------------------------------
225
226// This is a browser test for Geolocation.
227// It exercises various integration points from javascript <-> browser:
228// 1. Infobar is displayed when a geolocation is requested from an unauthorized
229// origin.
230// 2. Denying the infobar triggers the correct error callback.
231// 3. Allowing the infobar does not trigger an error, and allow a geoposition to
232// be passed to javascript.
233// 4. Permissions persisted in disk are respected.
234// 5. Incognito profiles don't use saved permissions.
235class GeolocationBrowserTest : public InProcessBrowserTest {
236 public:
237  enum InitializationOptions {
238    INITIALIZATION_NONE,
239    INITIALIZATION_OFFTHERECORD,
240    INITIALIZATION_NEWTAB,
241    INITIALIZATION_IFRAMES,
242  };
243
244  GeolocationBrowserTest();
245  virtual ~GeolocationBrowserTest();
246
247  // InProcessBrowserTest:
248  virtual void SetUpOnMainThread() OVERRIDE;
249  virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
250
251  Browser* current_browser() { return current_browser_; }
252  void set_html_for_tests(const std::string& html_for_tests) {
253    html_for_tests_ = html_for_tests;
254  }
255  content::RenderFrameHost* frame_host() const { return render_frame_host_; }
256  const GURL& current_url() const { return current_url_; }
257  const GURL& iframe_url(size_t i) const { return iframe_urls_[i]; }
258  double fake_latitude() const { return fake_latitude_; }
259  double fake_longitude() const { return fake_longitude_; }
260
261  // Initializes the test server and navigates to the initial url.
262  bool Initialize(InitializationOptions options) WARN_UNUSED_RESULT;
263
264  // Loads the specified number of iframes.
265  void LoadIFrames(int number_iframes);
266
267  // Specifies which frame is to be used for JavaScript calls.
268  void SetFrameHost(const std::string& frame_name);
269
270  // Start watching for geolocation notifications. If |wait_for_infobar| is
271  // true, wait for the infobar to be displayed. Otherwise wait for a javascript
272  // response.
273  void AddGeolocationWatch(bool wait_for_infobar);
274
275  // Checks that no errors have been received in javascript, and checks that the
276  // position most recently received in javascript matches |latitude| and
277  // |longitude|.
278  void CheckGeoposition(double latitude, double longitude);
279
280  // For |requesting_url| if |allowed| is true accept the infobar. Otherwise
281  // cancel it.
282  void SetInfoBarResponse(const GURL& requesting_url, bool allowed);
283
284  // Executes |function| in |render_frame_host| and checks that the return value
285  // matches |expected|.
286  void CheckStringValueFromJavascriptForFrame(
287      const std::string& expected,
288      const std::string& function,
289      content::RenderFrameHost* render_frame_host);
290
291  // Executes |function| and checks that the return value matches |expected|.
292  void CheckStringValueFromJavascript(const std::string& expected,
293                                      const std::string& function);
294
295  // Sets a new position and sends a notification with the new position.
296  void NotifyGeoposition(double latitude, double longitude);
297
298 private:
299  infobars::InfoBar* infobar_;
300  Browser* current_browser_;
301  // path element of a URL referencing the html content for this test.
302  std::string html_for_tests_;
303  // This member defines the frame where the JavaScript calls will run.
304  content::RenderFrameHost* render_frame_host_;
305  // The current url for the top level page.
306  GURL current_url_;
307  // If not empty, the GURLs for the iframes loaded by LoadIFrames().
308  std::vector<GURL> iframe_urls_;
309  double fake_latitude_;
310  double fake_longitude_;
311
312  DISALLOW_COPY_AND_ASSIGN(GeolocationBrowserTest);
313};
314
315GeolocationBrowserTest::GeolocationBrowserTest()
316  : infobar_(NULL),
317    current_browser_(NULL),
318    html_for_tests_("/geolocation/simple.html"),
319    render_frame_host_(NULL),
320    fake_latitude_(1.23),
321    fake_longitude_(4.56) {
322}
323
324GeolocationBrowserTest::~GeolocationBrowserTest() {
325}
326
327void GeolocationBrowserTest::SetUpOnMainThread() {
328  ui_test_utils::OverrideGeolocation(fake_latitude_, fake_longitude_);
329}
330
331void GeolocationBrowserTest::TearDownInProcessBrowserTestFixture() {
332  LOG(WARNING) << "TearDownInProcessBrowserTestFixture. Test Finished.";
333}
334
335bool GeolocationBrowserTest::Initialize(InitializationOptions options) {
336  if (!embedded_test_server()->Started() &&
337      !embedded_test_server()->InitializeAndWaitUntilReady()) {
338    ADD_FAILURE() << "Test server failed to start.";
339    return false;
340  }
341
342  current_url_ = embedded_test_server()->GetURL(html_for_tests_);
343  LOG(WARNING) << "before navigate";
344  if (options == INITIALIZATION_OFFTHERECORD) {
345    current_browser_ = ui_test_utils::OpenURLOffTheRecord(
346        browser()->profile(), current_url_);
347  } else {
348    current_browser_ = browser();
349    if (options == INITIALIZATION_NEWTAB)
350      chrome::NewTab(current_browser_);
351    ui_test_utils::NavigateToURL(current_browser_, current_url_);
352  }
353  LOG(WARNING) << "after navigate";
354
355  EXPECT_TRUE(current_browser_);
356  return !!current_browser_;
357}
358
359void GeolocationBrowserTest::LoadIFrames(int number_iframes) {
360  // Limit to 3 iframes.
361  DCHECK_LT(0, number_iframes);
362  DCHECK_LE(number_iframes, 3);
363  iframe_urls_.resize(number_iframes);
364  for (int i = 0; i < number_iframes; ++i) {
365    IFrameLoader loader(current_browser_, i, GURL());
366    iframe_urls_[i] = loader.iframe_url();
367  }
368}
369
370void GeolocationBrowserTest::SetFrameHost(const std::string& frame_name) {
371  WebContents* web_contents =
372      current_browser_->tab_strip_model()->GetActiveWebContents();
373  render_frame_host_ = NULL;
374
375  if (frame_name.empty()) {
376    render_frame_host_ = web_contents->GetMainFrame();
377  } else {
378    render_frame_host_ = content::FrameMatchingPredicate(
379        web_contents, base::Bind(&content::FrameMatchesName, frame_name));
380  }
381  DCHECK(render_frame_host_);
382}
383
384void GeolocationBrowserTest::AddGeolocationWatch(bool wait_for_infobar) {
385  GeolocationNotificationObserver notification_observer(wait_for_infobar);
386  notification_observer.AddWatchAndWaitForNotification(render_frame_host_);
387  if (wait_for_infobar) {
388    EXPECT_TRUE(notification_observer.has_infobar());
389    infobar_ = notification_observer.infobar();
390  }
391}
392
393void GeolocationBrowserTest::CheckGeoposition(double latitude,
394                                              double longitude) {
395  // Checks we have no error.
396  CheckStringValueFromJavascript("0", "geoGetLastError()");
397  CheckStringValueFromJavascript(base::DoubleToString(latitude),
398                                 "geoGetLastPositionLatitude()");
399  CheckStringValueFromJavascript(base::DoubleToString(longitude),
400                                 "geoGetLastPositionLongitude()");
401}
402
403void GeolocationBrowserTest::SetInfoBarResponse(const GURL& requesting_url,
404                                                bool allowed) {
405  WebContents* web_contents =
406      current_browser_->tab_strip_model()->GetActiveWebContents();
407  TabSpecificContentSettings* content_settings =
408      TabSpecificContentSettings::FromWebContents(web_contents);
409  const ContentSettingsUsagesState& usages_state =
410      content_settings->geolocation_usages_state();
411  size_t state_map_size = usages_state.state_map().size();
412  ASSERT_TRUE(infobar_);
413  LOG(WARNING) << "will set infobar response";
414  {
415    content::WindowedNotificationObserver observer(
416        content::NOTIFICATION_LOAD_STOP,
417        content::Source<NavigationController>(&web_contents->GetController()));
418    if (allowed)
419      infobar_->delegate()->AsConfirmInfoBarDelegate()->Accept();
420    else
421      infobar_->delegate()->AsConfirmInfoBarDelegate()->Cancel();
422    observer.Wait();
423  }
424
425  InfoBarService* infobar_service =
426      InfoBarService::FromWebContents(web_contents);
427  infobar_service->RemoveInfoBar(infobar_);
428  LOG(WARNING) << "infobar response set";
429  infobar_ = NULL;
430  EXPECT_GT(usages_state.state_map().size(), state_map_size);
431  GURL requesting_origin(requesting_url.GetOrigin());
432  EXPECT_EQ(1U, usages_state.state_map().count(requesting_origin));
433  ContentSetting expected_setting =
434        allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
435  EXPECT_EQ(expected_setting,
436            usages_state.state_map().find(requesting_origin)->second);
437}
438
439void GeolocationBrowserTest::CheckStringValueFromJavascriptForFrame(
440    const std::string& expected,
441    const std::string& function,
442    content::RenderFrameHost* render_frame_host) {
443  std::string script(base::StringPrintf(
444      "window.domAutomationController.send(%s)", function.c_str()));
445  std::string result;
446  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
447      render_frame_host, script, &result));
448  EXPECT_EQ(expected, result);
449}
450
451void GeolocationBrowserTest::CheckStringValueFromJavascript(
452    const std::string& expected,
453    const std::string& function) {
454  CheckStringValueFromJavascriptForFrame(
455      expected, function, render_frame_host_);
456}
457
458void GeolocationBrowserTest::NotifyGeoposition(double latitude,
459                                               double longitude) {
460  fake_latitude_ = latitude;
461  fake_longitude_ = longitude;
462  ui_test_utils::OverrideGeolocation(latitude, longitude);
463  LOG(WARNING) << "MockLocationProvider listeners updated";
464}
465
466
467// Tests ----------------------------------------------------------------------
468
469IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, DisplaysPermissionBar) {
470  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
471  SetFrameHost("");
472  AddGeolocationWatch(true);
473}
474
475IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, Geoposition) {
476  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
477  SetFrameHost("");
478  AddGeolocationWatch(true);
479  SetInfoBarResponse(current_url(), true);
480  CheckGeoposition(fake_latitude(), fake_longitude());
481}
482
483IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest,
484                       ErrorOnPermissionDenied) {
485  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
486  SetFrameHost("");
487  AddGeolocationWatch(true);
488  // Infobar was displayed, deny access and check for error code.
489  SetInfoBarResponse(current_url(), false);
490  CheckStringValueFromJavascript("1", "geoGetLastError()");
491}
492
493// See http://crbug.com/308358
494IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, DISABLED_NoInfobarForSecondTab) {
495  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
496  SetFrameHost("");
497  AddGeolocationWatch(true);
498  SetInfoBarResponse(current_url(), true);
499  // Disables further prompts from this tab.
500  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
501
502  // Checks infobar will not be created in a second tab.
503  ASSERT_TRUE(Initialize(INITIALIZATION_NEWTAB));
504  SetFrameHost("");
505  AddGeolocationWatch(false);
506  CheckGeoposition(fake_latitude(), fake_longitude());
507}
508
509IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, NoInfobarForDeniedOrigin) {
510  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
511  current_browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
512      ContentSettingsPattern::FromURLNoWildcard(current_url()),
513      ContentSettingsPattern::FromURLNoWildcard(current_url()),
514      CONTENT_SETTINGS_TYPE_GEOLOCATION, std::string(), CONTENT_SETTING_BLOCK);
515  SetFrameHost("");
516  AddGeolocationWatch(false);
517  // Checks we have an error for this denied origin.
518  CheckStringValueFromJavascript("1", "geoGetLastError()");
519  // Checks infobar will not be created a second tab.
520  ASSERT_TRUE(Initialize(INITIALIZATION_NEWTAB));
521  SetFrameHost("");
522  AddGeolocationWatch(false);
523  CheckStringValueFromJavascript("1", "geoGetLastError()");
524}
525
526IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, NoInfobarForAllowedOrigin) {
527  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
528  current_browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
529      ContentSettingsPattern::FromURLNoWildcard(current_url()),
530      ContentSettingsPattern::FromURLNoWildcard(current_url()),
531      CONTENT_SETTINGS_TYPE_GEOLOCATION, std::string(), CONTENT_SETTING_ALLOW);
532  // Checks no infobar will be created and there's no error callback.
533  SetFrameHost("");
534  AddGeolocationWatch(false);
535  CheckGeoposition(fake_latitude(), fake_longitude());
536}
537
538IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, NoInfobarForOffTheRecord) {
539  // First, check infobar will be created for regular profile
540  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
541  SetFrameHost("");
542  AddGeolocationWatch(true);
543  // Response will be persisted.
544  SetInfoBarResponse(current_url(), true);
545  CheckGeoposition(fake_latitude(), fake_longitude());
546  // Disables further prompts from this tab.
547  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
548  // Go incognito, and checks no infobar will be created.
549  ASSERT_TRUE(Initialize(INITIALIZATION_OFFTHERECORD));
550  SetFrameHost("");
551  AddGeolocationWatch(false);
552  CheckGeoposition(fake_latitude(), fake_longitude());
553}
554
555IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, NoLeakFromOffTheRecord) {
556  // First, check infobar will be created for incognito profile.
557  ASSERT_TRUE(Initialize(INITIALIZATION_OFFTHERECORD));
558  SetFrameHost("");
559  AddGeolocationWatch(true);
560  // Response won't be persisted.
561  SetInfoBarResponse(current_url(), true);
562  CheckGeoposition(fake_latitude(), fake_longitude());
563  // Disables further prompts from this tab.
564  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
565  // Go to the regular profile, infobar will be created.
566  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
567  SetFrameHost("");
568  AddGeolocationWatch(true);
569  SetInfoBarResponse(current_url(), false);
570  CheckStringValueFromJavascript("1", "geoGetLastError()");
571}
572
573IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, IFramesWithFreshPosition) {
574  set_html_for_tests("/geolocation/iframes_different_origin.html");
575  ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
576  LoadIFrames(2);
577  LOG(WARNING) << "frames loaded";
578
579  SetFrameHost("iframe_0");
580  AddGeolocationWatch(true);
581  SetInfoBarResponse(iframe_url(0), true);
582  CheckGeoposition(fake_latitude(), fake_longitude());
583  // Disables further prompts from this iframe.
584  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
585
586  // Test second iframe from a different origin with a cached geoposition will
587  // create the infobar.
588  SetFrameHost("iframe_1");
589  AddGeolocationWatch(true);
590
591  // Back to the first frame, enable navigation and refresh geoposition.
592  SetFrameHost("iframe_0");
593  CheckStringValueFromJavascript("1", "geoSetMaxNavigateCount(1)");
594  double fresh_position_latitude = 3.17;
595  double fresh_position_longitude = 4.23;
596  content::WindowedNotificationObserver observer(
597      content::NOTIFICATION_LOAD_STOP,
598      content::Source<NavigationController>(
599          &current_browser()->tab_strip_model()->GetActiveWebContents()->
600              GetController()));
601  NotifyGeoposition(fresh_position_latitude, fresh_position_longitude);
602  observer.Wait();
603  CheckGeoposition(fresh_position_latitude, fresh_position_longitude);
604
605  // Disable navigation for this frame.
606  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
607
608  // Now go ahead an authorize the second frame.
609  SetFrameHost("iframe_1");
610  // Infobar was displayed, allow access and check there's no error code.
611  SetInfoBarResponse(iframe_url(1), true);
612  LOG(WARNING) << "Checking position...";
613  CheckGeoposition(fresh_position_latitude, fresh_position_longitude);
614  LOG(WARNING) << "...done.";
615}
616
617IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest,
618                       IFramesWithCachedPosition) {
619  set_html_for_tests("/geolocation/iframes_different_origin.html");
620  ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
621  LoadIFrames(2);
622
623  SetFrameHost("iframe_0");
624  AddGeolocationWatch(true);
625  SetInfoBarResponse(iframe_url(0), true);
626  CheckGeoposition(fake_latitude(), fake_longitude());
627
628  // Refresh geoposition, but let's not yet create the watch on the second frame
629  // so that it'll fetch from cache.
630  double cached_position_latitude = 5.67;
631  double cached_position_lognitude = 8.09;
632  content::WindowedNotificationObserver observer(
633      content::NOTIFICATION_LOAD_STOP,
634      content::Source<NavigationController>(
635          &current_browser()->tab_strip_model()->GetActiveWebContents()->
636              GetController()));
637  NotifyGeoposition(cached_position_latitude, cached_position_lognitude);
638  observer.Wait();
639  CheckGeoposition(cached_position_latitude, cached_position_lognitude);
640
641  // Disable navigation for this frame.
642  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
643
644  // Now go ahead and authorize the second frame.
645  SetFrameHost("iframe_1");
646  AddGeolocationWatch(true);
647  // WebKit will use its cache, but we also broadcast a position shortly
648  // afterwards. We're only interested in the first navigation for the success
649  // callback from the cached position.
650  CheckStringValueFromJavascript("1", "geoSetMaxNavigateCount(1)");
651  SetInfoBarResponse(iframe_url(1), true);
652  CheckGeoposition(cached_position_latitude, cached_position_lognitude);
653}
654
655IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, CancelPermissionForFrame) {
656  set_html_for_tests("/geolocation/iframes_different_origin.html");
657  ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
658  LoadIFrames(2);
659  LOG(WARNING) << "frames loaded";
660
661  SetFrameHost("iframe_0");
662  AddGeolocationWatch(true);
663  SetInfoBarResponse(iframe_url(0), true);
664  CheckGeoposition(fake_latitude(), fake_longitude());
665  // Disables further prompts from this iframe.
666  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
667
668  // Test second iframe from a different origin with a cached geoposition will
669  // create the infobar.
670  SetFrameHost("iframe_1");
671  AddGeolocationWatch(true);
672
673  InfoBarService* infobar_service = InfoBarService::FromWebContents(
674      current_browser()->tab_strip_model()->GetActiveWebContents());
675  size_t num_infobars_before_cancel = infobar_service->infobar_count();
676  // Change the iframe, and ensure the infobar is gone.
677  IFrameLoader change_iframe_1(current_browser(), 1, current_url());
678  size_t num_infobars_after_cancel = infobar_service->infobar_count();
679  EXPECT_EQ(num_infobars_before_cancel, num_infobars_after_cancel + 1);
680}
681
682IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, InvalidUrlRequest) {
683  // Tests that an invalid URL (e.g. from a popup window) is rejected
684  // correctly. Also acts as a regression test for http://crbug.com/40478
685  set_html_for_tests("/geolocation/invalid_request_url.html");
686  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
687
688  SetFrameHost("");
689  WebContents* original_tab =
690      current_browser()->tab_strip_model()->GetActiveWebContents();
691  CheckStringValueFromJavascript("1", "requestGeolocationFromInvalidUrl()");
692  CheckStringValueFromJavascriptForFrame("1", "isAlive()",
693                                         original_tab->GetMainFrame());
694}
695
696IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, NoInfoBarBeforeStart) {
697  // See http://crbug.com/42789
698  set_html_for_tests("/geolocation/iframes_different_origin.html");
699  ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
700  LoadIFrames(2);
701  LOG(WARNING) << "frames loaded";
702
703  // Access navigator.geolocation, but ensure it won't request permission.
704  SetFrameHost("iframe_1");
705  CheckStringValueFromJavascript("object", "geoAccessNavigatorGeolocation()");
706
707  SetFrameHost("iframe_0");
708  AddGeolocationWatch(true);
709  SetInfoBarResponse(iframe_url(0), true);
710  CheckGeoposition(fake_latitude(), fake_longitude());
711  CheckStringValueFromJavascript("0", "geoSetMaxNavigateCount(0)");
712
713  // Permission should be requested after adding a watch.
714  SetFrameHost("iframe_1");
715  AddGeolocationWatch(true);
716  SetInfoBarResponse(iframe_url(1), true);
717  CheckGeoposition(fake_latitude(), fake_longitude());
718}
719
720IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, TwoWatchesInOneFrame) {
721  set_html_for_tests("/geolocation/two_watches.html");
722  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
723
724  // First, set the JavaScript to navigate when it receives |final_position|.
725  double final_position_latitude = 3.17;
726  double final_position_longitude = 4.23;
727  std::string script = base::StringPrintf(
728      "window.domAutomationController.send(geoSetFinalPosition(%f, %f))",
729      final_position_latitude, final_position_longitude);
730  std::string js_result;
731  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
732      current_browser()->tab_strip_model()->GetActiveWebContents(), script,
733      &js_result));
734  EXPECT_EQ(js_result, "ok");
735
736  // Send a position which both geolocation watches will receive.
737  SetFrameHost("");
738  AddGeolocationWatch(true);
739  SetInfoBarResponse(current_url(), true);
740  CheckGeoposition(fake_latitude(), fake_longitude());
741
742  // The second watch will now have cancelled. Ensure an update still makes
743  // its way through to the first watcher.
744  content::WindowedNotificationObserver observer(
745      content::NOTIFICATION_LOAD_STOP,
746      content::Source<NavigationController>(
747          &current_browser()->tab_strip_model()->GetActiveWebContents()->
748              GetController()));
749  NotifyGeoposition(final_position_latitude, final_position_longitude);
750  observer.Wait();
751  CheckGeoposition(final_position_latitude, final_position_longitude);
752}
753
754IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, TabDestroyed) {
755  set_html_for_tests("/geolocation/tab_destroyed.html");
756  ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
757  LoadIFrames(3);
758
759  SetFrameHost("iframe_0");
760  AddGeolocationWatch(true);
761
762  SetFrameHost("iframe_1");
763  AddGeolocationWatch(false);
764
765  SetFrameHost("iframe_2");
766  AddGeolocationWatch(false);
767
768  std::string script =
769      "window.domAutomationController.send(window.close());";
770  bool result = content::ExecuteScript(
771      current_browser()->tab_strip_model()->GetActiveWebContents(), script);
772  EXPECT_EQ(result, true);
773}
774
775IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, LastUsageUpdated) {
776  ASSERT_TRUE(Initialize(INITIALIZATION_NONE));
777  base::SimpleTestClock* clock_ = new base::SimpleTestClock();
778  current_browser()
779      ->profile()
780      ->GetHostContentSettingsMap()
781      ->SetPrefClockForTesting(scoped_ptr<base::Clock>(clock_));
782  clock_->SetNow(base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(10));
783
784  // Setting the permission should trigger the last usage.
785  current_browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
786      ContentSettingsPattern::FromURLNoWildcard(current_url()),
787      ContentSettingsPattern::FromURLNoWildcard(current_url()),
788      CONTENT_SETTINGS_TYPE_GEOLOCATION,
789      std::string(),
790      CONTENT_SETTING_ALLOW);
791
792  // Permission has been used at the starting time.
793  EXPECT_EQ(current_browser()
794                ->profile()
795                ->GetHostContentSettingsMap()
796                ->GetLastUsage(current_url().GetOrigin(),
797                               current_url().GetOrigin(),
798                               CONTENT_SETTINGS_TYPE_GEOLOCATION)
799                .ToDoubleT(),
800            10);
801
802  clock_->Advance(base::TimeDelta::FromSeconds(3));
803
804  // Watching should trigger the last usage update.
805  SetFrameHost("");
806  AddGeolocationWatch(false);
807  CheckGeoposition(fake_latitude(), fake_longitude());
808
809  // Last usage has been updated.
810  EXPECT_EQ(current_browser()
811                ->profile()
812                ->GetHostContentSettingsMap()
813                ->GetLastUsage(current_url().GetOrigin(),
814                               current_url().GetOrigin(),
815                               CONTENT_SETTINGS_TYPE_GEOLOCATION)
816                .ToDoubleT(),
817            13);
818}
819