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 "chrome/browser/extensions/extension_browsertest.h"
6#include "chrome/browser/ui/browser.h"
7#include "chrome/browser/ui/tabs/tab_strip_model.h"
8#include "chrome/common/url_constants.h"
9#include "chrome/test/base/ui_test_utils.h"
10#include "content/public/browser/web_contents.h"
11#include "content/public/test/browser_test_utils.h"
12#include "extensions/common/constants.h"
13#include "extensions/common/extension.h"
14
15using content::WebContents;
16using extensions::Extension;
17
18namespace {
19
20const char kSubscribePage[] = "/subscribe.html";
21const char kFeedPageMultiRel[] = "files/feeds/feed_multi_rel.html";
22const char kValidFeedNoLinks[] = "files/feeds/feed_nolinks.xml";
23const char kValidFeed0[] = "files/feeds/feed_script.xml";
24const char kValidFeed1[] = "files/feeds/feed1.xml";
25const char kValidFeed2[] = "files/feeds/feed2.xml";
26const char kValidFeed3[] = "files/feeds/feed3.xml";
27const char kValidFeed4[] = "files/feeds/feed4.xml";
28const char kValidFeed5[] = "files/feeds/feed5.xml";
29const char kValidFeed6[] = "files/feeds/feed6.xml";
30const char kInvalidFeed1[] = "files/feeds/feed_invalid1.xml";
31const char kInvalidFeed2[] = "files/feeds/feed_invalid2.xml";
32// We need a triple encoded string to prove that we are not decoding twice in
33// subscribe.js because one layer is also stripped off when subscribe.js passes
34// it to the XMLHttpRequest object.
35const char kFeedTripleEncoded[] = "files/feeds/url%25255Fdecoding.html";
36
37static const char kScriptFeedTitle[] =
38    "window.domAutomationController.send("
39    "  document.getElementById('title') ? "
40    "    document.getElementById('title').textContent : "
41    "    \"element 'title' not found\""
42    ");";
43static const char kScriptAnchor[] =
44    "window.domAutomationController.send("
45    "  document.getElementById('anchor_0') ? "
46    "    document.getElementById('anchor_0').textContent : "
47    "    \"element 'anchor_0' not found\""
48    ");";
49static const char kScriptDesc[] =
50    "window.domAutomationController.send("
51    "  document.getElementById('desc_0') ? "
52    "    document.getElementById('desc_0').textContent : "
53    "    \"element 'desc_0' not found\""
54    ");";
55static const char kScriptError[] =
56    "window.domAutomationController.send("
57    "  document.getElementById('error') ? "
58    "    document.getElementById('error').textContent : "
59    "    \"No error\""
60    ");";
61
62GURL GetFeedUrl(net::SpawnedTestServer* server, const std::string& feed_page,
63                bool direct_url, std::string extension_id) {
64  GURL feed_url = server->GetURL(feed_page);
65  if (direct_url) {
66    // We navigate directly to the subscribe page for feeds where the feed
67    // sniffing won't work, in other words, as is the case for malformed feeds.
68    return GURL(std::string(extensions::kExtensionScheme) +
69        url::kStandardSchemeSeparator +
70        extension_id + std::string(kSubscribePage) + std::string("?") +
71        feed_url.spec() + std::string("&synchronous"));
72  } else {
73    // Navigate to the feed content (which will cause the extension to try to
74    // sniff the type and display the subscribe page in another tab.
75    return GURL(feed_url.spec());
76  }
77}
78
79bool ValidatePageElement(content::RenderFrameHost* frame,
80                         const std::string& javascript,
81                         const std::string& expected_value) {
82  std::string returned_value;
83
84  if (!content::ExecuteScriptAndExtractString(frame,
85                                              javascript,
86                                              &returned_value))
87    return false;
88
89  EXPECT_STREQ(expected_value.c_str(), returned_value.c_str());
90  return expected_value == returned_value;
91}
92
93// Navigates to a feed page and, if |sniff_xml_type| is set, wait for the
94// extension to kick in, detect the feed and redirect to a feed preview page.
95// |sniff_xml_type| is generally set to true if the feed is sniffable and false
96// for invalid feeds.
97void NavigateToFeedAndValidate(net::SpawnedTestServer* server,
98                               const std::string& url,
99                               Browser* browser,
100                               std::string extension_id,
101                               bool sniff_xml_type,
102                               const std::string& expected_feed_title,
103                               const std::string& expected_item_title,
104                               const std::string& expected_item_desc,
105                               const std::string& expected_error) {
106  if (sniff_xml_type) {
107    // TODO(finnur): Implement this is a non-flaky way.
108  }
109
110  // Navigate to the subscribe page directly.
111  ui_test_utils::NavigateToURL(browser,
112                               GetFeedUrl(server, url, true, extension_id));
113
114  WebContents* tab = browser->tab_strip_model()->GetActiveWebContents();
115  content::RenderFrameHost* frame = content::FrameMatchingPredicate(
116      tab, base::Bind(&content::FrameIsChildOfMainFrame));
117  ASSERT_TRUE(ValidatePageElement(
118      tab->GetMainFrame(), kScriptFeedTitle, expected_feed_title));
119  ASSERT_TRUE(ValidatePageElement(frame, kScriptAnchor, expected_item_title));
120  ASSERT_TRUE(ValidatePageElement(frame, kScriptDesc, expected_item_desc));
121  ASSERT_TRUE(ValidatePageElement(frame, kScriptError, expected_error));
122}
123
124} // namespace
125
126// Makes sure that the RSS detects RSS feed links, even when rel tag contains
127// more than just "alternate".
128IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSMultiRelLink) {
129  ASSERT_TRUE(test_server()->Start());
130
131  ASSERT_TRUE(LoadExtension(
132    test_data_dir_.AppendASCII("subscribe_page_action")));
133
134  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0));
135
136  // Navigate to the feed page.
137  GURL feed_url = test_server()->GetURL(kFeedPageMultiRel);
138  ui_test_utils::NavigateToURL(browser(), feed_url);
139  // We should now have one page action ready to go in the LocationBar.
140  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
141}
142
143// This test is flaky on all platforms; see http://crbug.com/340354
144IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, DISABLED_RSSParseFeedValidFeed1) {
145  ASSERT_TRUE(test_server()->Start());
146
147  const Extension* extension = LoadExtension(
148      test_data_dir_.AppendASCII("subscribe_page_action"));
149  ASSERT_TRUE(extension);
150  std::string id = extension->id();
151
152  NavigateToFeedAndValidate(test_server(), kValidFeed1, browser(), id, true,
153                            "Feed for MyFeedTitle",
154                            "Title 1",
155                            "Desc",
156                            "No error");
157}
158
159IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed2) {
160  ASSERT_TRUE(test_server()->Start());
161
162  const Extension* extension = LoadExtension(
163      test_data_dir_.AppendASCII("subscribe_page_action"));
164  ASSERT_TRUE(extension);
165  std::string id = extension->id();
166
167  NavigateToFeedAndValidate(test_server(), kValidFeed2, browser(), id, true,
168                            "Feed for MyFeed2",
169                            "My item title1",
170                            "This is a summary.",
171                            "No error");
172}
173
174IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed3) {
175  ASSERT_TRUE(test_server()->Start());
176
177  const Extension* extension = LoadExtension(
178      test_data_dir_.AppendASCII("subscribe_page_action"));
179  ASSERT_TRUE(extension);
180  std::string id = extension->id();
181
182  NavigateToFeedAndValidate(test_server(), kValidFeed3, browser(), id, true,
183                            "Feed for Google Code buglist rss feed",
184                            "My dear title",
185                            "My dear content",
186                            "No error");
187}
188
189IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed4) {
190  ASSERT_TRUE(test_server()->Start());
191
192  const Extension* extension = LoadExtension(
193      test_data_dir_.AppendASCII("subscribe_page_action"));
194  ASSERT_TRUE(extension);
195  std::string id = extension->id();
196
197  NavigateToFeedAndValidate(test_server(), kValidFeed4, browser(), id, true,
198                            "Feed for Title chars <script> %23 stop",
199                            "Title chars  %23 stop",
200                            "My dear content %23 stop",
201                            "No error");
202}
203
204IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed0) {
205  ASSERT_TRUE(test_server()->Start());
206
207  const Extension* extension = LoadExtension(
208      test_data_dir_.AppendASCII("subscribe_page_action"));
209  ASSERT_TRUE(extension);
210  std::string id = extension->id();
211
212  // Try a feed with a link with an onclick handler (before r27440 this would
213  // trigger a NOTREACHED).
214  NavigateToFeedAndValidate(test_server(), kValidFeed0, browser(), id, true,
215                            "Feed for MyFeedTitle",
216                            "Title 1",
217                            "Desc VIDEO",
218                            "No error");
219}
220
221IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed5) {
222  ASSERT_TRUE(test_server()->Start());
223
224  const Extension* extension = LoadExtension(
225      test_data_dir_.AppendASCII("subscribe_page_action"));
226  ASSERT_TRUE(extension);
227  std::string id = extension->id();
228
229  // Feed with valid but mostly empty xml.
230  NavigateToFeedAndValidate(test_server(), kValidFeed5, browser(), id, true,
231                            "Feed for Unknown feed name",
232                            "element 'anchor_0' not found",
233                            "element 'desc_0' not found",
234                            "This feed contains no entries.");
235}
236
237IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed6) {
238  ASSERT_TRUE(test_server()->Start());
239
240  const Extension* extension = LoadExtension(
241      test_data_dir_.AppendASCII("subscribe_page_action"));
242  ASSERT_TRUE(extension);
243  std::string id = extension->id();
244
245  // Feed that is technically invalid but still parseable.
246  NavigateToFeedAndValidate(test_server(), kValidFeed6, browser(), id, true,
247                            "Feed for MyFeedTitle",
248                            "Title 1",
249                            "Desc",
250                            "No error");
251}
252
253IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed1) {
254  ASSERT_TRUE(test_server()->Start());
255
256  const Extension* extension = LoadExtension(
257      test_data_dir_.AppendASCII("subscribe_page_action"));
258  ASSERT_TRUE(extension);
259  std::string id = extension->id();
260
261  // Try an empty feed.
262  NavigateToFeedAndValidate(test_server(), kInvalidFeed1, browser(), id, false,
263                            "Feed for Unknown feed name",
264                            "element 'anchor_0' not found",
265                            "element 'desc_0' not found",
266                            "This feed contains no entries.");
267}
268
269IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed2) {
270  ASSERT_TRUE(test_server()->Start());
271
272  const Extension* extension = LoadExtension(
273      test_data_dir_.AppendASCII("subscribe_page_action"));
274  ASSERT_TRUE(extension);
275  std::string id = extension->id();
276
277  // Try a garbage feed.
278  NavigateToFeedAndValidate(test_server(), kInvalidFeed2, browser(), id, false,
279                            "Feed for Unknown feed name",
280                            "element 'anchor_0' not found",
281                            "element 'desc_0' not found",
282                            "This feed contains no entries.");
283}
284
285IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed3) {
286  ASSERT_TRUE(test_server()->Start());
287
288  const Extension* extension = LoadExtension(
289      test_data_dir_.AppendASCII("subscribe_page_action"));
290  ASSERT_TRUE(extension);
291  std::string id = extension->id();
292
293  // Try a feed that doesn't exist.
294  NavigateToFeedAndValidate(test_server(), "foo.xml", browser(), id, false,
295                            "Feed for Unknown feed name",
296                            "element 'anchor_0' not found",
297                            "element 'desc_0' not found",
298                            "This feed contains no entries.");
299}
300
301IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed4) {
302  ASSERT_TRUE(test_server()->Start());
303
304  const Extension* extension = LoadExtension(
305      test_data_dir_.AppendASCII("subscribe_page_action"));
306  ASSERT_TRUE(extension);
307  std::string id = extension->id();
308
309  // subscribe.js shouldn't double-decode the URL passed in. Otherwise feed
310  // links such as http://search.twitter.com/search.atom?lang=en&q=%23chrome
311  // will result in no feed being downloaded because %23 gets decoded to # and
312  // therefore #chrome is not treated as part of the Twitter query. This test
313  // uses an underscore instead of a hash, but the principle is the same. If
314  // we start erroneously double decoding again, the path (and the feed) will
315  // become valid resulting in a failure for this test.
316  NavigateToFeedAndValidate(
317      test_server(), kFeedTripleEncoded, browser(), id, true,
318      "Feed for Unknown feed name",
319      "element 'anchor_0' not found",
320      "element 'desc_0' not found",
321      "This feed contains no entries.");
322}
323
324IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeedNoLinks) {
325  ASSERT_TRUE(test_server()->Start());
326
327  const Extension* extension = LoadExtension(
328      test_data_dir_.AppendASCII("subscribe_page_action"));
329  ASSERT_TRUE(extension);
330  std::string id = extension->id();
331
332  // Valid feed but containing no links.
333  NavigateToFeedAndValidate(
334      test_server(), kValidFeedNoLinks, browser(), id, true,
335      "Feed for MyFeedTitle",
336      "Title with no link",
337      "Desc",
338      "No error");
339}
340