summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorfinnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-09 21:30:53 +0000
committerfinnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-09 21:30:53 +0000
commit361b28a80cfd2a9a0db454c7ebdb9c95ea55840c (patch)
treea6cf253e64ded952e1a5518f8d25ac8848c6200b /chrome
parent76b118a0f798a2e88103492173225e57b2062a37 (diff)
downloadchromium_src-361b28a80cfd2a9a0db454c7ebdb9c95ea55840c.zip
chromium_src-361b28a80cfd2a9a0db454c7ebdb9c95ea55840c.tar.gz
chromium_src-361b28a80cfd2a9a0db454c7ebdb9c95ea55840c.tar.bz2
Add a rudamentary feed preview to the RSS extension. It
doesn't handle inline HTML in the item description (it just dumps it as text) and the feed needs to be valid XML for it to show any preview, but it is better than nothing. We can easily change it to display the HTML but we want to (at some point) try to use a separate origin so that we can render the HTML code from untrusted sources safely. Also fix a bug in the image tracker. It should not try to communicate with the view if the view has gone away (which was the whole point of the image tracker...) BUG=None TEST=Install the extension, browse to a page with a feed and click onthe rss icon in the Omnibox. An interstitial page should appear with a preview of the feed. Review URL: http://codereview.chromium.org/155180 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20316 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/cocoa/location_bar_view_mac.h15
-rw-r--r--chrome/browser/cocoa/location_bar_view_mac.mm5
-rw-r--r--chrome/browser/extensions/extension_browsertest.cc25
-rw-r--r--chrome/browser/extensions/extension_browsertest.h3
-rw-r--r--chrome/browser/extensions/extension_browsertests_misc.cc136
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.cc5
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.h11
-rw-r--r--chrome/browser/location_bar.h10
-rw-r--r--chrome/browser/views/location_bar_view.cc12
-rw-r--r--chrome/browser/views/location_bar_view.h5
-rw-r--r--chrome/test/data/extensions/samples/subscribe_page_action/background.html6
-rw-r--r--chrome/test/data/extensions/samples/subscribe_page_action/manifest.json4
-rw-r--r--chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html230
-rw-r--r--chrome/test/data/feeds/feed.html8
-rw-r--r--chrome/test/data/feeds/feed1.xml10
-rw-r--r--chrome/test/data/feeds/feed2.xml9
-rw-r--r--chrome/test/data/feeds/feed_invalid1.xml0
-rw-r--r--chrome/test/data/feeds/feed_invalid2.xml1
-rw-r--r--chrome/test/data/feeds/no_feed.html7
-rw-r--r--chrome/test/test_location_bar.h3
20 files changed, 482 insertions, 23 deletions
diff --git a/chrome/browser/cocoa/location_bar_view_mac.h b/chrome/browser/cocoa/location_bar_view_mac.h
index f863e51..26cd8fb 100644
--- a/chrome/browser/cocoa/location_bar_view_mac.h
+++ b/chrome/browser/cocoa/location_bar_view_mac.h
@@ -20,7 +20,8 @@ class ToolbarModel;
// the location bar text field, which handles most of the work.
class LocationBarViewMac : public AutocompleteEditController,
- public LocationBar {
+ public LocationBar,
+ public LocationBarTesting {
public:
LocationBarViewMac(NSTextField* field,
CommandUpdater* command_updater,
@@ -28,7 +29,7 @@ class LocationBarViewMac : public AutocompleteEditController,
Profile* profile);
virtual ~LocationBarViewMac();
- // Overridden from LocationBar
+ // Overridden from LocationBar:
virtual void ShowFirstRunBubble(bool use_OEM_bubble) { NOTIMPLEMENTED(); }
virtual std::wstring GetInputString() const;
virtual WindowOpenDisposition GetWindowOpenDisposition() const;
@@ -44,6 +45,10 @@ class LocationBarViewMac : public AutocompleteEditController,
virtual AutocompleteEditView* location_entry() {
return edit_view_.get();
}
+ virtual LocationBarTesting* GetLocationBarForTesting() { return this; }
+
+ // Overriden from LocationBarTesting:
+ virtual int PageActionVisibleCount();
// Updates the location bar. Resets the bar's permanent text and
// security style, and if |should_restore_state| is true, restores
@@ -62,17 +67,17 @@ class LocationBarViewMac : public AutocompleteEditController,
private:
scoped_ptr<AutocompleteEditViewMac> edit_view_;
- CommandUpdater* command_updater_; // weak, owned by Browser
+ CommandUpdater* command_updater_; // Weak, owned by Browser.
// When we get an OnAutocompleteAccept notification from the autocomplete
// edit, we save the input string so we can give it back to the browser on
// the LocationBar interface via GetInputString().
std::wstring location_input_;
- // The user's desired disposition for how their input should be opened
+ // The user's desired disposition for how their input should be opened.
WindowOpenDisposition disposition_;
- // The transition type to use for the navigation
+ // The transition type to use for the navigation.
PageTransition::Type transition_;
DISALLOW_COPY_AND_ASSIGN(LocationBarViewMac);
diff --git a/chrome/browser/cocoa/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar_view_mac.mm
index ddf0c30..4409ba6 100644
--- a/chrome/browser/cocoa/location_bar_view_mac.mm
+++ b/chrome/browser/cocoa/location_bar_view_mac.mm
@@ -132,3 +132,8 @@ std::wstring LocationBarViewMac::GetTitle() const {
void LocationBarViewMac::Revert() {
edit_view_->RevertAll();
}
+
+int LocationBarViewMac::PageActionVisibleCount() {
+ NOTIMPLEMENTED();
+ return -1;
+}
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index cec4a74..7ef3239 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -4,6 +4,8 @@
#include "base/file_path.h"
#include "base/path_service.h"
#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/location_bar.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/profile.h"
@@ -96,6 +98,29 @@ void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
service->UninstallExtension(extension_id, false);
}
+bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(
+ int count) {
+ base::Time start_time = base::Time::Now();
+ while (true) {
+ LocationBarTesting* loc_bar =
+ browser()->window()->GetLocationBar()->GetLocationBarForTesting();
+
+ int visible = loc_bar->PageActionVisibleCount();
+ if (visible == count)
+ return true;
+
+ if ((base::Time::Now() - start_time).InMilliseconds() > kTimeoutMs)
+ return false;
+
+ std::cout << "Timed out waiting for page actions to become visible."
+ << "Currently visible page actions: " << IntToString(visible);
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ new MessageLoop::QuitTask, 200);
+ ui_test_utils::RunMessageLoop();
+ }
+}
+
bool ExtensionBrowserTest::WaitForExtensionHostsToLoad() {
// Wait for all the extension hosts that exist to finish loading.
// NOTE: This assumes that the extension host list is not changing while
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 385fd9e..5990f5c 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -25,6 +25,9 @@ class ExtensionBrowserTest
bool InstallExtension(const FilePath& path);
void UninstallExtension(const std::string& extension_id);
+ // Wait for the number of visible page actions to change to |count|.
+ bool WaitForPageActionVisibilityChangeTo(int count);
+
bool loaded_;
bool installed_;
FilePath test_data_dir_;
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc
index 0211b96..f17e5d5 100644
--- a/chrome/browser/extensions/extension_browsertests_misc.cc
+++ b/chrome/browser/extensions/extension_browsertests_misc.cc
@@ -117,6 +117,140 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TabContents) {
EXPECT_TRUE(result);
}
+// Tests that we can load page actions in the Omnibox.
+IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageAction) {
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("samples")
+ .AppendASCII("subscribe_page_action")));
+
+ ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0));
+
+ // Navigate to the feed page.
+ FilePath test_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_dir);
+ FilePath feed = test_dir.AppendASCII("feeds")
+ .AppendASCII("feed.html");
+
+ ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL(feed));
+
+ // We should now have one page action ready to go in the LocationBar.
+ ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
+
+ FilePath no_feed = test_dir.AppendASCII("feeds")
+ .AppendASCII("nofeed.html");
+
+ // Make sure the page action goes away.
+ ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL(no_feed));
+ ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0));
+}
+
+GURL GetFeedUrl(const std::string& feed_page) {
+ FilePath test_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_dir);
+
+ FilePath subscribe;
+ subscribe = test_dir.AppendASCII("extensions")
+ .AppendASCII("samples")
+ .AppendASCII("subscribe_page_action")
+ .AppendASCII("subscribe.html");
+ subscribe = subscribe.StripTrailingSeparators();
+
+ FilePath feed_dir = test_dir.AppendASCII("feeds")
+ .AppendASCII(feed_page.c_str());
+
+ return GURL(net::FilePathToFileURL(subscribe).spec() +
+ std::string("?") +
+ net::FilePathToFileURL(feed_dir).spec());
+}
+
+static const wchar_t* jscript_feed_title =
+ L"window.domAutomationController.send("
+ L" document.getElementById('title') ? "
+ L" document.getElementById('title').textContent : "
+ L" \"element 'title' not found\""
+ L");";
+static const wchar_t* jscript_anchor =
+ L"window.domAutomationController.send("
+ L" document.getElementById('anchor_0') ? "
+ L" document.getElementById('anchor_0').textContent : "
+ L" \"element 'anchor_0' not found\""
+ L");";
+static const wchar_t* jscript_desc =
+ L"window.domAutomationController.send("
+ L" document.getElementById('desc_0') ? "
+ L" document.getElementById('desc_0').textContent : "
+ L" \"element 'desc_0' not found\""
+ L");";
+static const wchar_t* jscript_error =
+ L"window.domAutomationController.send("
+ L" document.getElementById('error') ? "
+ L" document.getElementById('error').textContent : "
+ L" \"No error\""
+ L");";
+
+void GetParsedFeedData(Browser* browser, std::string* feed_title,
+ std::string* item_title, std::string* item_desc,
+ std::string* error) {
+ ui_test_utils::ExecuteJavaScriptAndExtractString(
+ browser->GetSelectedTabContents()->render_view_host(), L"",
+ jscript_feed_title, feed_title);
+ ui_test_utils::ExecuteJavaScriptAndExtractString(
+ browser->GetSelectedTabContents()->render_view_host(), L"",
+ jscript_anchor, item_title);
+ ui_test_utils::ExecuteJavaScriptAndExtractString(
+ browser->GetSelectedTabContents()->render_view_host(), L"",
+ jscript_desc, item_desc);
+ ui_test_utils::ExecuteJavaScriptAndExtractString(
+ browser->GetSelectedTabContents()->render_view_host(), L"",
+ jscript_error, error);
+}
+
+// Tests that we can parse feeds.
+IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeed) {
+ std::string feed_title;
+ std::string item_title;
+ std::string item_desc;
+ std::string error;
+
+ ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed1.xml"));
+ GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error);
+ EXPECT_STREQ("Feed for 'MyFeedTitle'", feed_title.c_str());
+ EXPECT_STREQ("Title 1", item_title.c_str());
+ EXPECT_STREQ("Desc", item_desc.c_str());
+ EXPECT_STREQ("No error", error.c_str());
+
+ ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed2.xml"));
+ GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error);
+ EXPECT_STREQ("Feed for 'MyFeed2'", feed_title.c_str());
+ EXPECT_STREQ("My item title1", item_title.c_str());
+ EXPECT_STREQ("This is a summary.", item_desc.c_str());
+ EXPECT_STREQ("No error", error.c_str());
+
+ // Try a feed that doesn't exist.
+ ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed_nonexistant.xml"));
+ GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error);
+ EXPECT_STREQ("Feed for 'Unknown feed name'", feed_title.c_str());
+ EXPECT_STREQ("element 'anchor_0' not found", item_title.c_str());
+ EXPECT_STREQ("element 'desc_0' not found", item_desc.c_str());
+ EXPECT_STREQ("Not a valid feed", error.c_str());
+
+ // Try an empty feed.
+ ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed_invalid1.xml"));
+ GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error);
+ EXPECT_STREQ("Feed for 'Unknown feed name'", feed_title.c_str());
+ EXPECT_STREQ("element 'anchor_0' not found", item_title.c_str());
+ EXPECT_STREQ("element 'desc_0' not found", item_desc.c_str());
+ EXPECT_STREQ("Not a valid feed", error.c_str());
+
+ // Try a garbage feed.
+ ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed_invalid2.xml"));
+ GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error);
+ EXPECT_STREQ("Feed for 'Unknown feed name'", feed_title.c_str());
+ EXPECT_STREQ("element 'anchor_0' not found", item_title.c_str());
+ EXPECT_STREQ("element 'desc_0' not found", item_desc.c_str());
+ EXPECT_STREQ("Not a valid feed", error.c_str());
+}
+
// Tests that message passing between extensions and tabs works.
IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MessagingExtensionTab) {
ASSERT_TRUE(LoadExtension(
@@ -214,4 +348,4 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MessagingContentScript) {
host->render_view_host(), L"", L"testDisconnectOnClose()", &result);
EXPECT_TRUE(result);
}
-#endif \ No newline at end of file
+#endif
diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc
index 49f799b..fe4f548 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/gtk/location_bar_view_gtk.cc
@@ -357,6 +357,11 @@ void LocationBarViewGtk::Revert() {
location_entry_->RevertAll();
}
+int LocationBarViewGtk::PageActionVisibleCount() {
+ NOTIMPLEMENTED();
+ return -1;
+}
+
gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget,
GdkEventExpose* event) {
GdkDrawable* drawable = GDK_DRAWABLE(event->window);
diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h
index a04e94f..3ccbbb2 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.h
+++ b/chrome/browser/gtk/location_bar_view_gtk.h
@@ -27,7 +27,8 @@ class TabContents;
class ToolbarModel;
class LocationBarViewGtk : public AutocompleteEditController,
- public LocationBar {
+ public LocationBar,
+ public LocationBarTesting {
public:
LocationBarViewGtk(CommandUpdater* command_updater,
ToolbarModel* toolbar_model,
@@ -71,6 +72,10 @@ class LocationBarViewGtk : public AutocompleteEditController,
virtual AutocompleteEditView* location_entry() {
return location_entry_.get();
}
+ virtual LocationBarTesting* GetLocationBarForTesting() { return this; }
+
+ // Implement the LocationBarTesting interface.
+ virtual int PageActionVisibleCount();
// Translation between a security level and the background color. Both the
// location bar and edit have to manage and match the background color.
@@ -130,10 +135,10 @@ class LocationBarViewGtk : public AutocompleteEditController,
// the LocationBar interface via GetInputString().
std::wstring location_input_;
- // The user's desired disposition for how their input should be opened
+ // The user's desired disposition for how their input should be opened.
WindowOpenDisposition disposition_;
- // The transition type to use for the navigation
+ // The transition type to use for the navigation.
PageTransition::Type transition_;
DISALLOW_COPY_AND_ASSIGN(LocationBarViewGtk);
diff --git a/chrome/browser/location_bar.h b/chrome/browser/location_bar.h
index 218f65c..c8e9293 100644
--- a/chrome/browser/location_bar.h
+++ b/chrome/browser/location_bar.h
@@ -17,6 +17,7 @@
#include "webkit/glue/window_open_disposition.h"
class AutocompleteEditView;
+class LocationBarTesting;
class TabContents;
class LocationBar {
@@ -60,6 +61,15 @@ class LocationBar {
// Returns a pointer to the text entry view.
virtual AutocompleteEditView* location_entry() = 0;
+
+ // Returns a pointer to the testing interface.
+ virtual LocationBarTesting* GetLocationBarForTesting() = 0;
+};
+
+class LocationBarTesting {
+ public:
+ // Returns the number of visible page actions in the Omnibox.
+ virtual int PageActionVisibleCount() = 0;
};
#endif // CHROME_BROWSER_LOCATION_BAR_H_
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index af8c57f..eb6d629 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -1153,7 +1153,8 @@ class LocationBarView::PageActionImageView::ImageLoadingTracker
}
void OnImageLoaded(SkBitmap* image, int index) {
- view_->OnImageLoaded(image, index);
+ if (view_)
+ view_->OnImageLoaded(image, index);
delete image;
if (--image_count_ == 0)
Release(); // We are no longer needed.
@@ -1366,3 +1367,12 @@ void LocationBarView::SaveStateToContents(TabContents* contents) {
void LocationBarView::Revert() {
location_entry_->RevertAll();
}
+
+int LocationBarView::PageActionVisibleCount() {
+ int result = 0;
+ for (size_t i = 0; i < page_action_image_views_.size(); i++) {
+ if (page_action_image_views_[i]->IsVisible())
+ ++result;
+ }
+ return result;
+}
diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h
index 97e7eb6..d5d82e4 100644
--- a/chrome/browser/views/location_bar_view.h
+++ b/chrome/browser/views/location_bar_view.h
@@ -41,6 +41,7 @@ class Profile;
//
/////////////////////////////////////////////////////////////////////////////
class LocationBarView : public LocationBar,
+ public LocationBarTesting,
public views::View,
public AutocompleteEditController {
public:
@@ -132,6 +133,10 @@ class LocationBarView : public LocationBar,
virtual AutocompleteEditView* location_entry() {
return location_entry_.get();
}
+ virtual LocationBarTesting* GetLocationBarForTesting() { return this; }
+
+ // Overridden from LocationBarTesting:
+ virtual int PageActionVisibleCount();
static const int kVertMargin;
static const SkColor kBackgroundColorByLevel[];
diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/background.html b/chrome/test/data/extensions/samples/subscribe_page_action/background.html
index 0eeca7e..94761ff 100644
--- a/chrome/test/data/extensions/samples/subscribe_page_action/background.html
+++ b/chrome/test/data/extensions/samples/subscribe_page_action/background.html
@@ -91,6 +91,12 @@
// feed URL.
chrome.tabs.create({url: "subscribe.html?" + feedUrl,
windowId: windowId});
+ } else {
+ console.log("Ignoring execute event.");
+ console.log("PageActionId: " + reply.pageActionId + " == " +
+ pageActionId);
+ console.log("TabUrl : " + reply.data.tabUrl + " == " +
+ pageUrl);
}
}
});
diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json b/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json
index 6a8f225..5807816 100644
--- a/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json
+++ b/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json
@@ -1,14 +1,14 @@
{
"name": "RSS Subscription Extension",
"description": "Adds one-click subscription to your toolbar",
- "version": "1.0",
+ "version": "1.1",
"permissions": [
"http://*/*"
],
"background_page": "background.html",
"content_scripts": [
{
- "matches": ["http://*/*"],
+ "matches": ["http://*/*", "file://*.*"],
"js": ["feed_finder.js"]
}
],
diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html b/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html
index 011b6c9..54860bb 100644
--- a/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html
+++ b/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html
@@ -3,22 +3,230 @@
<title>Subscribe to feed</title>
<script type="text/javascript">
+ // Grab the URL from the querystring, removing question mark at the front.
+ var feedUrl = document.location.search.substring(1);
+
+ // The XMLHttpRequest object that tries to load and parse the feed.
+ var req;
+
+ // What to show when we cannot parse the feed name.
+ var unknownName = "Unknown feed name";
+
+ // The maximum number of feed items to show in the preview.
+ var maxFeedItems = 10;
+
+ // The maximum number of characters to show in the feed item title.
+ var maxTitleCount = 64;
+
+ // The maximum number of characters to show for the feed item description.
+ var maxDescCount = 1024;
+
+ // Navigates to the reader of the user's choice (for subscribing to the feed).
function navigate() {
- var select = document.getElementById("readers");
- // Grab the URL from the querystring, removing question mark at the front.
- var feed_url = document.location.search.substring(1);
- var url = select.options[select.selectedIndex].value + feed_url;
+ var select = document.getElementById('readers');
+ var url = select.options[select.selectedIndex].value + feedUrl;
document.location = url;
}
+
+ // Parses the feed specified. We will either end up in handleResponse or
+ // handleError from here.
+ function loadFeed() {
+ feedUrl = decodeURIComponent(feedUrl);
+ req = new XMLHttpRequest();
+ req.onload = handleResponse;
+ req.onerror = handleError;
+ req.open("GET", feedUrl, false);
+ req.send(null);
+ }
+
+ // Sets the title for the feed.
+ function setFeedTitle(title) {
+ var titleTag = document.getElementById('title');
+ titleTag.textContent = "Feed for '" + title + "'";
+ }
+
+ // Handles errors during the XMLHttpRequest.
+ function handleError() {
+ handleFeedParsingFailed("Error fetching feed");
+ }
+
+ // Handles feed parsing errors.
+ function handleFeedParsingFailed(error) {
+ setFeedTitle(unknownName);
+
+ // First part of the error message.
+ var spanErrorPrefix = document.createElement('span');
+ spanErrorPrefix.setAttribute('class', 'item_desc');
+ spanErrorPrefix.appendChild(
+ document.createTextNode("Unable to show feed preview ("));
+
+ // The actual error.
+ var spanError = document.createElement('span');
+ spanError.id = 'error';
+ spanError.setAttribute('class', 'item_desc');
+ spanError.appendChild(document.createTextNode(error));
+
+ // Closing braces, etc.
+ var spanErrorSuffix = document.createElement('span');
+ spanErrorSuffix.setAttribute('class', 'item_desc');
+ spanErrorSuffix.appendChild(
+ document.createTextNode(")."));
+
+ // Add the error message.
+ var itemsTag = document.getElementById('items');
+ itemsTag.appendChild(spanErrorPrefix);
+ itemsTag.appendChild(spanError);
+ itemsTag.appendChild(spanErrorSuffix);
+ }
+
+ // Handles parsing the feed data we got back from XMLHttpRequest.
+ function handleResponse() {
+ var itemsTag = document.getElementById('items');
+
+ // Uncomment these two lines to see what the feed data looks like.
+ // itemsTag.textContent = req.responseText;
+ // return;
+
+ var doc = req.responseXML;
+ if (!doc) {
+ handleFeedParsingFailed("Not a valid feed");
+ return;
+ }
+
+ // Figure out what the title of the whole feed is.
+ var title = doc.getElementsByTagName('title')[0];
+ if (title)
+ setFeedTitle(title.textContent);
+ else
+ setFeedTitle(unknownName);
+
+ // Now parse the rest. Some use <entry> for each feed item, others use
+ // <channel><item>.
+ var entries = doc.getElementsByTagName('entry');
+ if (entries.length == 0)
+ entries = doc.getElementsByTagName('item');
+
+ for (i = 0; i < entries.length && i < maxFeedItems; ++i) {
+ item = entries.item(i);
+
+ // Grab the title for the feed item.
+ var itemTitle = item.getElementsByTagName('title')[0];
+ if (itemTitle)
+ itemTitle = itemTitle.textContent;
+ else
+ itemTitle = "Unknown title";
+
+ // Ensure max length for title.
+ if (itemTitle.length > maxTitleCount)
+ itemTitle = itemTitle.substring(0, maxTitleCount) + "...";
+
+ // Grab the description.
+ var itemDesc = item.getElementsByTagName('description')[0];
+ if (!itemDesc)
+ itemDesc = item.getElementsByTagName('summary')[0];
+
+ if (itemDesc)
+ itemDesc = itemDesc.textContent;
+ else
+ itemDesc = "";
+
+ // Ensure max length for description.
+ if (itemDesc.length > maxDescCount)
+ itemDesc = itemDesc.substring(0, maxDescCount) + "...";
+
+ // Grab the link URL.
+ var itemLink = item.getElementsByTagName('link');
+ var link = itemLink[0].childNodes[0];
+ if (link)
+ link = itemLink[0].childNodes[0].nodeValue;
+ else
+ link = itemLink[0].getAttribute('href');
+
+ // Create an anchor node for showing the title.
+ var anchorTitle = document.createElement('a');
+ anchorTitle.id = 'anchor_' + String(i);
+ anchorTitle.setAttribute('class', 'item_title');
+ anchorTitle.setAttribute('href', link);
+ anchorTitle.setAttribute('target', '_blank');
+ anchorTitle.appendChild(document.createTextNode(itemTitle));
+
+ // Create a description node.
+ var spanDesc = document.createElement('span');
+ spanDesc.id = 'desc_' + String(i);
+ spanDesc.setAttribute('class', 'item_desc');
+ spanDesc.appendChild(document.createTextNode(itemDesc));
+
+ // Add this to the document.
+ itemsTag.appendChild(anchorTitle);
+ itemsTag.appendChild(document.createElement('br'));
+ itemsTag.appendChild(spanDesc);
+ itemsTag.appendChild(document.createElement('br'));
+ itemsTag.appendChild(document.createElement('br'));
+ }
+ }
</script>
+
+<style>
+body {
+ font-family: arial, sans-serif;
+ font-size: 11px;
+}
+a {
+ color: #2244D2;
+ font-weight:bold;
+ text-decoration: none;
+}
+a:hover {
+ font-weight:bold;
+ text-decoration: underline;
+}
+
+.splitter {
+ padding: 2px 8px 2px 5px;
+ border-top: solid 1px #9CC2EF;
+ background: #EBEFF9;
+ font-size: 13px;
+ font-weight:bold;
+}
+.item_title {
+ font-size: 12px;
+}
+.item_desc {
+ font-size: 12px;
+}
+</style>
</head>
-<body>
- <img src="feed-icon-64x64.png" align="absmiddle" />
- Subscribe to this feed using:
- <select id="readers">
- <option value="http://www.google.com/reader/view/feed/">Google</option>
- </select>
- <button onclick="navigate();">Subscribe Now</button>
+<body onload="loadFeed();">
+ <table>
+ <tr>
+ <td width="75">
+ <img src="feed-icon-64x64.png" alt="feed-icon" align="absmiddle" />
+ </td>
+ <td>
+ <b><span id="title"></span></b><br>
+ <span>Subscribe to this feed using:</span>
+ <select id="readers">
+ <option value="http://www.google.com/reader/view/feed/">
+ Google Reader
+ </option>
+ <option value="http://www.google.com/ig/adde?moduleurl=">
+ iGoogle
+ </option>
+ <option value="http://www.bloglines.com/login?r=/sub/">
+ Bloglines
+ </option>
+ <option value="http://add.my.yahoo.com/rss?url=">
+ My Yahoo
+ </option>
+ </select>
+ <button onclick="navigate();">Subscribe Now</button>
+ </td>
+ </tr>
+ </table>
+
+ <div>&nbsp;</div>
+ <div class="splitter"><b>Feed preview</b></div><br>
+ <div id="items"></div>
</body>
</html>
diff --git a/chrome/test/data/feeds/feed.html b/chrome/test/data/feeds/feed.html
new file mode 100644
index 0000000..a776481
--- /dev/null
+++ b/chrome/test/data/feeds/feed.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <link rel="alternate" type="application/rss+xml" title="RSS" href="feed1.xml"/>
+</head>
+<body>
+ This is a page with a feed link.
+</body>
+</html>
diff --git a/chrome/test/data/feeds/feed1.xml b/chrome/test/data/feeds/feed1.xml
new file mode 100644
index 0000000..b480fdf
--- /dev/null
+++ b/chrome/test/data/feeds/feed1.xml
@@ -0,0 +1,10 @@
+<rss version="2.0">
+<channel>
+ <title>MyFeedTitle</title>
+ <item>
+ <title>Title 1</title>
+ <link>http://www.google.com</link>
+ <description>Desc</description>
+ </item>
+</channel>
+</rss> \ No newline at end of file
diff --git a/chrome/test/data/feeds/feed2.xml b/chrome/test/data/feeds/feed2.xml
new file mode 100644
index 0000000..aff3bf2
--- /dev/null
+++ b/chrome/test/data/feeds/feed2.xml
@@ -0,0 +1,9 @@
+<feed>
+ <title>MyFeed2</title>
+
+ <entry>
+ <title>My item title1</title>
+ <link rel="alternate" type="text/html" href="http://www.google.com" />
+ <summary>This is a summary.</summary>
+ </entry>
+</feed> \ No newline at end of file
diff --git a/chrome/test/data/feeds/feed_invalid1.xml b/chrome/test/data/feeds/feed_invalid1.xml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/feeds/feed_invalid1.xml
diff --git a/chrome/test/data/feeds/feed_invalid2.xml b/chrome/test/data/feeds/feed_invalid2.xml
new file mode 100644
index 0000000..b24b52b
--- /dev/null
+++ b/chrome/test/data/feeds/feed_invalid2.xml
@@ -0,0 +1 @@
+This is a garbage feed. \ No newline at end of file
diff --git a/chrome/test/data/feeds/no_feed.html b/chrome/test/data/feeds/no_feed.html
new file mode 100644
index 0000000..9151a8c
--- /dev/null
+++ b/chrome/test/data/feeds/no_feed.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+<body>
+ This is a page with no feed link.
+</body>
+</html>
diff --git a/chrome/test/test_location_bar.h b/chrome/test/test_location_bar.h
index 95ce2c1..995d07a 100644
--- a/chrome/test/test_location_bar.h
+++ b/chrome/test/test_location_bar.h
@@ -43,6 +43,9 @@ class TestLocationBar : public LocationBar {
virtual AutocompleteEditView* location_entry() {
return NULL;
}
+ virtual LocationBarTesting* GetLocationBarForTesting() {
+ return NULL;
+ }
private: