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 "content/public/test/test_navigation_observer.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "base/stl_util.h"
11#include "content/public/browser/notification_service.h"
12#include "content/public/browser/notification_types.h"
13#include "content/public/browser/web_contents_observer.h"
14#include "content/public/test/test_utils.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace content {
18
19class TestNavigationObserver::TestWebContentsObserver
20    : public WebContentsObserver {
21 public:
22  TestWebContentsObserver(TestNavigationObserver* parent,
23                          WebContents* web_contents)
24      : WebContentsObserver(web_contents),
25        parent_(parent) {
26  }
27
28 private:
29  // WebContentsObserver:
30  virtual void NavigationEntryCommitted(
31      const LoadCommittedDetails& load_details) OVERRIDE {
32    parent_->OnNavigationEntryCommitted(this, web_contents(), load_details);
33  }
34
35  virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
36    parent_->OnWebContentsDestroyed(this, web_contents);
37  }
38
39  TestNavigationObserver* parent_;
40
41  DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
42};
43
44TestNavigationObserver::TestNavigationObserver(
45    WebContents* web_contents,
46    int number_of_navigations)
47    : navigation_started_(false),
48      navigations_completed_(0),
49      number_of_navigations_(number_of_navigations),
50      done_(false),
51      running_(false),
52      web_contents_created_callback_(
53          base::Bind(
54              &TestNavigationObserver::OnWebContentsCreated,
55              base::Unretained(this))) {
56  if (web_contents)
57    RegisterAsObserver(web_contents);
58}
59
60TestNavigationObserver::TestNavigationObserver(
61    WebContents* web_contents)
62    : navigation_started_(false),
63      navigations_completed_(0),
64      number_of_navigations_(1),
65      done_(false),
66      running_(false),
67      web_contents_created_callback_(
68          base::Bind(
69              &TestNavigationObserver::OnWebContentsCreated,
70              base::Unretained(this))) {
71  if (web_contents)
72    RegisterAsObserver(web_contents);
73}
74
75TestNavigationObserver::~TestNavigationObserver() {
76  StopWatchingNewWebContents();
77
78  STLDeleteContainerPointers(web_contents_observers_.begin(),
79                             web_contents_observers_.end());
80}
81
82void TestNavigationObserver::WaitForObservation(
83    const base::Closure& wait_loop_callback,
84    const base::Closure& done_callback) {
85  if (done_)
86    return;
87
88  EXPECT_FALSE(running_);
89  running_ = true;
90  done_callback_ = done_callback;
91  wait_loop_callback.Run();
92  EXPECT_TRUE(done_);
93}
94
95void TestNavigationObserver::Wait() {
96  base::RunLoop run_loop;
97  WaitForObservation(
98      base::Bind(&base::RunLoop::Run, base::Unretained(&run_loop)),
99      GetQuitTaskForRunLoop(&run_loop));
100}
101
102void TestNavigationObserver::StartWatchingNewWebContents() {
103  WebContents::AddCreatedCallback(web_contents_created_callback_);
104}
105
106void TestNavigationObserver::StopWatchingNewWebContents() {
107  WebContents::RemoveCreatedCallback(web_contents_created_callback_);
108}
109
110void TestNavigationObserver::RegisterAsObserver(
111    WebContents* web_contents) {
112  web_contents_observers_.insert(
113      new TestWebContentsObserver(this, web_contents));
114
115  NotificationSource notification_source =
116      Source<NavigationController>(&web_contents->GetController());
117  registrar_.Add(this, NOTIFICATION_LOAD_START, notification_source);
118  registrar_.Add(this, NOTIFICATION_LOAD_STOP, notification_source);
119}
120
121void TestNavigationObserver::Observe(
122    int type, const NotificationSource& source,
123    const NotificationDetails& details) {
124  switch (type) {
125    case NOTIFICATION_LOAD_START:
126      navigation_started_ = true;
127      break;
128    case NOTIFICATION_LOAD_STOP:
129      if (navigation_started_ &&
130          ++navigations_completed_ == number_of_navigations_) {
131        navigation_started_ = false;
132        done_ = true;
133        if (running_) {
134          running_ = false;
135          done_callback_.Run();
136        }
137      }
138      break;
139    default:
140      NOTREACHED();
141  }
142}
143
144void TestNavigationObserver::OnWebContentsCreated(WebContents* web_contents) {
145  RegisterAsObserver(web_contents);
146}
147
148void TestNavigationObserver::OnWebContentsDestroyed(
149    TestWebContentsObserver* observer,
150    WebContents* web_contents) {
151  web_contents_observers_.erase(observer);
152  delete observer;
153
154  NotificationSource notification_source =
155      Source<NavigationController>(&web_contents->GetController());
156  registrar_.Remove(this, NOTIFICATION_LOAD_START, notification_source);
157  registrar_.Remove(this, NOTIFICATION_LOAD_STOP, notification_source);
158}
159
160void TestNavigationObserver::OnNavigationEntryCommitted(
161    TestWebContentsObserver* observer,
162    WebContents* web_contents,
163    const LoadCommittedDetails& load_details) {
164  navigation_started_ = true;
165}
166
167}  // namespace content
168