Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ios / web / web_state / web_state_impl_unittest.mm
blob3c88084cf3f9342752281f52663a95119bbd084d
1 // Copyright 2014 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.
5 #include "base/bind.h"
6 #include "base/logging.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/values.h"
9 #include "ios/web/public/load_committed_details.h"
10 #include "ios/web/public/test/test_browser_state.h"
11 #include "ios/web/public/web_state/web_state_observer.h"
12 #include "ios/web/public/web_state/web_state_policy_decider.h"
13 #include "ios/web/web_state/web_state_impl.h"
14 #include "net/http/http_response_headers.h"
15 #include "net/http/http_util.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "testing/gtest_mac.h"
19 #include "testing/platform_test.h"
20 #include "url/gurl.h"
22 using testing::_;
23 using testing::Assign;
24 using testing::AtMost;
25 using testing::DoAll;
26 using testing::Return;
28 namespace web {
29 namespace {
31 // Test observer to check that the WebStateObserver methods are called as
32 // expected.
33 class TestWebStateObserver : public WebStateObserver {
34  public:
35   TestWebStateObserver(WebState* web_state)
36       : WebStateObserver(web_state),
37         provisional_navigation_started_called_(false),
38         navigation_item_committed_called_(false),
39         page_loaded_called_(false),
40         url_hash_changed_called_(false),
41         history_state_changed_called_(false),
42         web_state_destroyed_called_(false) {}
44   // Methods returning true if the corresponding WebStateObserver method has
45   // been called.
46   bool provisional_navigation_started_called() {
47     return provisional_navigation_started_called_;
48   };
49   bool navigation_item_committed_called() {
50     return navigation_item_committed_called_;
51   }
52   bool page_loaded_called() { return page_loaded_called_; }
53   bool url_hash_changed_called() { return url_hash_changed_called_; }
54   bool history_state_changed_called() { return history_state_changed_called_; }
55   bool web_state_destroyed_called() { return web_state_destroyed_called_; }
57  private:
58   // WebStateObserver implementation:
59   void ProvisionalNavigationStarted(const GURL& url) override {
60     provisional_navigation_started_called_ = true;
61   }
62   void NavigationItemCommitted(
63       const LoadCommittedDetails& load_details) override {
64     navigation_item_committed_called_ = true;
65   }
66   void PageLoaded(PageLoadCompletionStatus load_completion_status) override {
67     page_loaded_called_ =
68         load_completion_status == PageLoadCompletionStatus::SUCCESS;
69   }
70   void UrlHashChanged() override { url_hash_changed_called_ = true; }
71   void HistoryStateChanged() override { history_state_changed_called_ = true; }
72   void WebStateDestroyed() override {
73     web_state_destroyed_called_ = true;
74     Observe(nullptr);
75   }
77   bool provisional_navigation_started_called_;
78   bool navigation_item_committed_called_;
79   bool page_loaded_called_;
80   bool url_hash_changed_called_;
81   bool history_state_changed_called_;
82   bool web_state_destroyed_called_;
85 // Test decider to check that the WebStatePolicyDecider methods are called as
86 // expected.
87 class MockWebStatePolicyDecider : public WebStatePolicyDecider {
88  public:
89   explicit MockWebStatePolicyDecider(WebState* web_state)
90       : WebStatePolicyDecider(web_state) {}
91   virtual ~MockWebStatePolicyDecider() {}
93   MOCK_METHOD1(ShouldAllowRequest, bool(NSURLRequest* request));
94   MOCK_METHOD1(ShouldAllowResponse, bool(NSURLResponse* response));
95   MOCK_METHOD0(WebStateDestroyed, void());
98 // Creates and returns an HttpResponseHeader using the string representation.
99 scoped_refptr<net::HttpResponseHeaders> HeadersFromString(const char* string) {
100   std::string raw_string(string);
101   std::string headers_string = net::HttpUtil::AssembleRawHeaders(
102       raw_string.c_str(), raw_string.length());
103   scoped_refptr<net::HttpResponseHeaders> headers(
104       new net::HttpResponseHeaders(headers_string));
105   return headers;
108 // Test callback for script commands.
109 // Sets |is_called| to true if it is called, and checks that the parameters
110 // match their expected values.
111 // |user_is_interacting| is not checked because Bind() has a maximum of 7
112 // parameters.
113 bool HandleScriptCommand(bool* is_called,
114                          bool should_handle,
115                          base::DictionaryValue* expected_value,
116                          const GURL& expected_url,
117                          const base::DictionaryValue& value,
118                          const GURL& url,
119                          bool user_is_interacting) {
120   *is_called = true;
121   EXPECT_TRUE(expected_value->Equals(&value));
122   EXPECT_EQ(expected_url, url);
123   return should_handle;
126 class WebStateTest : public PlatformTest {
127  protected:
128   void SetUp() override {
129     web_state_.reset(new WebStateImpl(&browser_state_));
130     web_state_->SetWebController(nil);
131   }
133   web::TestBrowserState browser_state_;
134   scoped_ptr<WebStateImpl> web_state_;
137 TEST_F(WebStateTest, ResponseHeaders) {
138   GURL real_url("http://foo.com/bar");
139   GURL frame_url("http://frames-r-us.com/");
140   scoped_refptr<net::HttpResponseHeaders> real_headers(HeadersFromString(
141       "HTTP/1.1 200 OK\r\n"
142       "Content-Type: text/html\r\n"
143       "Content-Language: en\r\n"
144       "X-Should-Be-Here: yep\r\n"
145       "\r\n"));
146   scoped_refptr<net::HttpResponseHeaders> frame_headers(HeadersFromString(
147       "HTTP/1.1 200 OK\r\n"
148       "Content-Type: application/pdf\r\n"
149       "Content-Language: fr\r\n"
150       "X-Should-Not-Be-Here: oops\r\n"
151       "\r\n"));
152   // Simulate a load of a page with a frame.
153   web_state_->OnHttpResponseHeadersReceived(real_headers.get(), real_url);
154   web_state_->OnHttpResponseHeadersReceived(frame_headers.get(), frame_url);
155   // Include a hash to be sure it's handled correctly.
156   web_state_->OnPageLoaded(GURL(real_url.spec() + std::string("#baz")), true);
158   // Verify that the right header set was kept.
159   EXPECT_TRUE(
160       web_state_->GetHttpResponseHeaders()->HasHeader("X-Should-Be-Here"));
161   EXPECT_FALSE(
162       web_state_->GetHttpResponseHeaders()->HasHeader("X-Should-Not-Be-Here"));
164   // And that it was parsed correctly.
165   EXPECT_EQ("text/html", web_state_->GetContentsMimeType());
166   EXPECT_EQ("en", web_state_->GetContentLanguageHeader());
169 TEST_F(WebStateTest, ResponseHeaderClearing) {
170   GURL url("http://foo.com/");
171   scoped_refptr<net::HttpResponseHeaders> headers(HeadersFromString(
172       "HTTP/1.1 200 OK\r\n"
173       "Content-Type: text/html\r\n"
174       "Content-Language: en\r\n"
175       "\r\n"));
176   web_state_->OnHttpResponseHeadersReceived(headers.get(), url);
178   // There should be no headers before loading.
179   EXPECT_EQ(NULL, web_state_->GetHttpResponseHeaders());
181   // There should be headers and parsed values after loading.
182   web_state_->OnPageLoaded(url, true);
183   EXPECT_TRUE(web_state_->GetHttpResponseHeaders()->HasHeader("Content-Type"));
184   EXPECT_NE("", web_state_->GetContentsMimeType());
185   EXPECT_NE("", web_state_->GetContentLanguageHeader());
187   // ... but not after loading another page, nor should there be specific
188   // parsed values.
189   web_state_->OnPageLoaded(GURL("http://elsewhere.com/"), true);
190   EXPECT_EQ(NULL, web_state_->GetHttpResponseHeaders());
191   EXPECT_EQ("", web_state_->GetContentsMimeType());
192   EXPECT_EQ("", web_state_->GetContentLanguageHeader());
195 TEST_F(WebStateTest, ObserverTest) {
196   scoped_ptr<TestWebStateObserver> observer(
197       new TestWebStateObserver(web_state_.get()));
198   EXPECT_EQ(web_state_.get(), observer->web_state());
200   // Test that ProvisionalNavigationStarted() is called.
201   EXPECT_FALSE(observer->provisional_navigation_started_called());
202   web_state_->OnProvisionalNavigationStarted(GURL("http://test"));
203   EXPECT_TRUE(observer->provisional_navigation_started_called());
205   // Test that NavigtionItemCommitted() is called.
206   EXPECT_FALSE(observer->navigation_item_committed_called());
207   LoadCommittedDetails details;
208   web_state_->OnNavigationItemCommitted(details);
209   EXPECT_TRUE(observer->navigation_item_committed_called());
211   // Test that DidFinishLoad() is called, only when there is no error.
212   EXPECT_FALSE(observer->page_loaded_called());
213   web_state_->OnPageLoaded(GURL("http://test"), false);
214   EXPECT_FALSE(observer->page_loaded_called());
215   web_state_->OnPageLoaded(GURL("http://test"), true);
216   EXPECT_TRUE(observer->page_loaded_called());
218   // Test that UrlHashChanged() is called.
219   EXPECT_FALSE(observer->url_hash_changed_called());
220   web_state_->OnUrlHashChanged();
221   EXPECT_TRUE(observer->url_hash_changed_called());
223   // Test that HistoryStateChanged() is called.
224   EXPECT_FALSE(observer->history_state_changed_called());
225   web_state_->OnHistoryStateChanged();
226   EXPECT_TRUE(observer->history_state_changed_called());
228   // Test that WebStateDestroyed() is called.
229   EXPECT_FALSE(observer->web_state_destroyed_called());
230   web_state_.reset();
231   EXPECT_TRUE(observer->web_state_destroyed_called());
233   EXPECT_EQ(nullptr, observer->web_state());
236 // Verifies that policy deciders are correctly called by the web state.
237 TEST_F(WebStateTest, PolicyDeciderTest) {
238   MockWebStatePolicyDecider decider(web_state_.get());
239   MockWebStatePolicyDecider decider2(web_state_.get());
240   EXPECT_EQ(web_state_.get(), decider.web_state());
242   // Test that ShouldAllowRequest() is called.
243   EXPECT_CALL(decider, ShouldAllowRequest(_)).Times(1).WillOnce(Return(true));
244   EXPECT_CALL(decider2, ShouldAllowRequest(_)).Times(1).WillOnce(Return(true));
245   EXPECT_TRUE(web_state_->ShouldAllowRequest(nil));
247   // Test that ShouldAllowRequest() is stopping on negative answer. Only one
248   // one the decider should be called.
249   {
250     bool decider_called = false;
251     bool decider2_called = false;
252     EXPECT_CALL(decider, ShouldAllowRequest(_))
253         .Times(AtMost(1))
254         .WillOnce(DoAll(Assign(&decider_called, true), Return(false)));
255     EXPECT_CALL(decider2, ShouldAllowRequest(_))
256         .Times(AtMost(1))
257         .WillOnce(DoAll(Assign(&decider2_called, true), Return(false)));
258     EXPECT_FALSE(web_state_->ShouldAllowRequest(nil));
259     EXPECT_FALSE(decider_called && decider2_called);
260   }
262   // Test that ShouldAllowResponse() is called.
263   EXPECT_CALL(decider, ShouldAllowResponse(_)).Times(1).WillOnce(Return(true));
264   EXPECT_CALL(decider2, ShouldAllowResponse(_)).Times(1).WillOnce(Return(true));
265   EXPECT_TRUE(web_state_->ShouldAllowResponse(nil));
267   // Test that ShouldAllowResponse() is stopping on negative answer. Only one
268   // one the decider should be called.
269   {
270     bool decider_called = false;
271     bool decider2_called = false;
272     EXPECT_CALL(decider, ShouldAllowResponse(_))
273         .Times(AtMost(1))
274         .WillOnce(DoAll(Assign(&decider_called, true), Return(false)));
275     EXPECT_CALL(decider2, ShouldAllowResponse(_))
276         .Times(AtMost(1))
277         .WillOnce(DoAll(Assign(&decider2_called, true), Return(false)));
278     EXPECT_FALSE(web_state_->ShouldAllowResponse(nil));
279     EXPECT_FALSE(decider_called && decider2_called);
280   }
282   // Test that WebStateDestroyed() is called.
283   EXPECT_CALL(decider, WebStateDestroyed()).Times(1);
284   EXPECT_CALL(decider2, WebStateDestroyed()).Times(1);
285   web_state_.reset();
286   EXPECT_EQ(nullptr, decider.web_state());
289 // Tests that script command callbacks are called correctly.
290 TEST_F(WebStateTest, ScriptCommand) {
291   // Set up two script command callbacks.
292   const std::string kPrefix1("prefix1");
293   const std::string kCommand1("prefix1.command1");
294   base::DictionaryValue value_1;
295   value_1.SetString("a", "b");
296   const GURL kUrl1("http://foo");
297   bool is_called_1 = false;
298   web_state_->AddScriptCommandCallback(
299       base::Bind(&HandleScriptCommand, &is_called_1, true, &value_1, kUrl1),
300       kPrefix1);
302   const std::string kPrefix2("prefix2");
303   const std::string kCommand2("prefix2.command2");
304   base::DictionaryValue value_2;
305   value_2.SetString("c", "d");
306   const GURL kUrl2("http://bar");
307   bool is_called_2 = false;
308   web_state_->AddScriptCommandCallback(
309       base::Bind(&HandleScriptCommand, &is_called_2, false, &value_2, kUrl2),
310       kPrefix2);
312   // Check that a irrelevant or invalid command does not trigger the callbacks.
313   EXPECT_FALSE(
314       web_state_->OnScriptCommandReceived("wohoo.blah", value_1, kUrl1, false));
315   EXPECT_FALSE(is_called_1);
316   EXPECT_FALSE(is_called_2);
318   EXPECT_FALSE(web_state_->OnScriptCommandReceived(
319       "prefix1ButMissingDot", value_1, kUrl1, false));
320   EXPECT_FALSE(is_called_1);
321   EXPECT_FALSE(is_called_2);
323   // Check that only the callback matching the prefix is called, with the
324   // expected parameters and return value;
325   EXPECT_TRUE(
326       web_state_->OnScriptCommandReceived(kCommand1, value_1, kUrl1, false));
327   EXPECT_TRUE(is_called_1);
328   EXPECT_FALSE(is_called_2);
330   // Remove the callback and check it is no longer called.
331   is_called_1 = false;
332   web_state_->RemoveScriptCommandCallback(kPrefix1);
333   EXPECT_FALSE(
334       web_state_->OnScriptCommandReceived(kCommand1, value_1, kUrl1, false));
335   EXPECT_FALSE(is_called_1);
336   EXPECT_FALSE(is_called_2);
338   // Check that a false return value is forwarded correctly.
339   EXPECT_FALSE(
340       web_state_->OnScriptCommandReceived(kCommand2, value_2, kUrl2, false));
341   EXPECT_FALSE(is_called_1);
342   EXPECT_TRUE(is_called_2);
344   web_state_->RemoveScriptCommandCallback(kPrefix2);
347 }  // namespace
348 }  // namespace web