Web Intents: Hook up the register intent InfoBar with the WebIntentsRegistry.

BUG=none
TEST=RegisterIntentHandlerInfoBarDelegateTest.Accept

[email protected]

Review URL: http://codereview.chromium.org/7601013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96449 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/intents/register_intent_handler_infobar_delegate.cc b/chrome/browser/intents/register_intent_handler_infobar_delegate.cc
index 2097e3e..cac9b915 100644
--- a/chrome/browser/intents/register_intent_handler_infobar_delegate.cc
+++ b/chrome/browser/intents/register_intent_handler_infobar_delegate.cc
@@ -5,14 +5,20 @@
 #include "chrome/browser/intents/register_intent_handler_infobar_delegate.h"
 
 #include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/intents/web_intents_registry.h"
+#include "chrome/browser/intents/web_intents_registry_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "content/browser/tab_contents/tab_contents.h"
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 RegisterIntentHandlerInfoBarDelegate::RegisterIntentHandlerInfoBarDelegate(
-    TabContents* tab_contents)
+    TabContents* tab_contents, const WebIntentData& intent)
     : ConfirmInfoBarDelegate(tab_contents),
-      tab_contents_(tab_contents) {
+      tab_contents_(tab_contents),
+      profile_(Profile::FromBrowserContext(tab_contents->browser_context())),
+      intent_(intent) {
 }
 
 InfoBarDelegate::Type
@@ -21,21 +27,30 @@
 }
 
 string16 RegisterIntentHandlerInfoBarDelegate::GetMessageText() const {
-  return l10n_util::GetStringFUTF16(IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM,
-                                    string16(), string16());
+  return l10n_util::GetStringFUTF16(
+      IDS_REGISTER_INTENT_HANDLER_CONFIRM,
+      intent_.title,
+      UTF8ToUTF16(intent_.service_url.host()));
 }
 
 string16 RegisterIntentHandlerInfoBarDelegate::GetButtonLabel(
     InfoBarButton button) const {
   if (button == BUTTON_OK) {
     return l10n_util::GetStringFUTF16(IDS_REGISTER_INTENT_HANDLER_ACCEPT,
-                                      string16());
+                                      UTF8ToUTF16(intent_.service_url.host()));
   }
 
   DCHECK(button == BUTTON_CANCEL);
   return l10n_util::GetStringUTF16(IDS_REGISTER_INTENT_HANDLER_DENY);
 }
 
+bool RegisterIntentHandlerInfoBarDelegate::Accept() {
+  WebIntentsRegistry* registry =
+      WebIntentsRegistryFactory::GetForProfile(profile_);
+  registry->RegisterIntentProvider(intent_);
+  return true;
+}
+
 string16 RegisterIntentHandlerInfoBarDelegate::GetLinkText() const {
   return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
 }
diff --git a/chrome/browser/intents/register_intent_handler_infobar_delegate.h b/chrome/browser/intents/register_intent_handler_infobar_delegate.h
index 315691a..0c74071 100644
--- a/chrome/browser/intents/register_intent_handler_infobar_delegate.h
+++ b/chrome/browser/intents/register_intent_handler_infobar_delegate.h
@@ -8,21 +8,24 @@
 
 #include "base/basictypes.h"
 #include "base/string16.h"
+#include "chrome/browser/intents/web_intent_data.h"
 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
 
+class Profile;
 class TabContents;
 
 // The InfoBar used to request permission for a site to be registered as an
 // Intent handler.
 class RegisterIntentHandlerInfoBarDelegate : public ConfirmInfoBarDelegate {
  public:
-  // TODO(jhawkins): Pass in WebIntentData.
-  explicit RegisterIntentHandlerInfoBarDelegate(TabContents* tab_contents);
+  RegisterIntentHandlerInfoBarDelegate(TabContents* tab_contents,
+                                       const WebIntentData& intent);
 
   // ConfirmInfoBarDelegate implementation.
   virtual Type GetInfoBarType() const OVERRIDE;
   virtual string16 GetMessageText() const OVERRIDE;
   virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
+  virtual bool Accept() OVERRIDE;
   virtual string16 GetLinkText() const OVERRIDE;
   virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE;
 
@@ -30,6 +33,12 @@
   // The TabContents that contains this InfoBar. Weak pointer.
   TabContents* tab_contents_;
 
+  // The profile associated with |tab_contents_|. Weak pointer.
+  Profile* profile_;
+
+  // The cached intent data bundle passed up from the renderer.
+  WebIntentData intent_;
+
   DISALLOW_COPY_AND_ASSIGN(RegisterIntentHandlerInfoBarDelegate);
 };
 
diff --git a/chrome/browser/intents/register_intent_handler_infobar_delegate_unittest.cc b/chrome/browser/intents/register_intent_handler_infobar_delegate_unittest.cc
new file mode 100644
index 0000000..48e0639
--- /dev/null
+++ b/chrome/browser/intents/register_intent_handler_infobar_delegate_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/intents/register_intent_handler_infobar_delegate.h"
+#include "chrome/browser/intents/web_intents_registry.h"
+#include "chrome/browser/intents/web_intents_registry_factory.h"
+#include "chrome/browser/intents/web_intent_data.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/testing_browser_process_test.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/renderer_host/test_render_view_host.h"
+#include "content/browser/site_instance.h"
+#include "content/browser/tab_contents/test_tab_contents.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockWebIntentsRegistry : public WebIntentsRegistry {
+ public:
+  MOCK_METHOD1(RegisterIntentProvider, void(const WebIntentData&));
+};
+
+ProfileKeyedService* BuildMockWebIntentsRegistry(Profile* profile) {
+  return new MockWebIntentsRegistry;
+}
+
+MockWebIntentsRegistry* BuildForProfile(Profile* profile) {
+  return static_cast<MockWebIntentsRegistry*>(
+      WebIntentsRegistryFactory::GetInstance()->SetTestingFactoryAndUse(
+          profile, BuildMockWebIntentsRegistry));
+}
+
+class RegisterIntentHandlerInfoBarDelegateTest
+    : public RenderViewHostTestHarness {
+ protected:
+  RegisterIntentHandlerInfoBarDelegateTest()
+      : ui_thread_(BrowserThread::UI, MessageLoopForUI::current()) {}
+
+  virtual void SetUp() {
+    RenderViewHostTestHarness::SetUp();
+
+    profile_.reset(new TestingProfile);
+    profile_->CreateWebDataService(false);
+
+    SiteInstance* instance = SiteInstance::CreateSiteInstance(profile_.get());
+    tab_contents_.reset(new TestTabContents(profile_.get(), instance));
+
+    web_intents_registry_ = BuildForProfile(profile_.get());
+  }
+
+  virtual void TearDown() {
+    tab_contents_.reset();
+    web_intents_registry_ = NULL;
+    profile_.reset();
+
+    RenderViewHostTestHarness::TearDown();
+  }
+
+  scoped_ptr<TestTabContents> tab_contents_;
+  MockWebIntentsRegistry* web_intents_registry_;
+
+ private:
+  BrowserThread ui_thread_;
+  scoped_ptr<TestingProfile> profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(RegisterIntentHandlerInfoBarDelegateTest);
+};
+
+TEST_F(RegisterIntentHandlerInfoBarDelegateTest, Accept) {
+  WebIntentData intent;
+  intent.service_url = GURL("google.com");
+  intent.action = ASCIIToUTF16("http://webintents.org/share");
+  intent.type = ASCIIToUTF16("text/url");
+  RegisterIntentHandlerInfoBarDelegate delegate(tab_contents_.get(), intent);
+
+  EXPECT_CALL(*web_intents_registry_, RegisterIntentProvider(intent));
+  delegate.Accept();
+}
+
+}  // namespace
diff --git a/chrome/browser/intents/web_intent_data.cc b/chrome/browser/intents/web_intent_data.cc
new file mode 100644
index 0000000..3f7339f
--- /dev/null
+++ b/chrome/browser/intents/web_intent_data.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/intents/web_intent_data.h"
+
+WebIntentData::WebIntentData() {}
+
+WebIntentData::~WebIntentData() {}
+
+bool WebIntentData::operator==(const WebIntentData& other) const {
+  return (service_url == other.service_url &&
+          action == other.action &&
+          type == other.type);
+}
diff --git a/chrome/browser/intents/web_intent_data.h b/chrome/browser/intents/web_intent_data.h
index 9d4b006..b920e7ce 100644
--- a/chrome/browser/intents/web_intent_data.h
+++ b/chrome/browser/intents/web_intent_data.h
@@ -11,9 +11,15 @@
 
 // Describes the relevant elements of a WebIntent.
 struct WebIntentData {
+  WebIntentData();
+  ~WebIntentData();
+
+  bool operator==(const WebIntentData& other) const;
+
   GURL service_url;  // URL for service invocation.
   string16 action;  // Name of action provided by service.
   string16 type;  // MIME type of data accepted by service.
+  string16 title;  // The title of the service.
 };
 
 #endif  // CHROME_BROWSER_INTENTS_WEB_INTENT_DATA_H_
diff --git a/chrome/browser/intents/web_intents_registry.h b/chrome/browser/intents/web_intents_registry.h
index 76c01db..02ab4fd 100644
--- a/chrome/browser/intents/web_intents_registry.h
+++ b/chrome/browser/intents/web_intents_registry.h
@@ -9,11 +9,14 @@
 #include "base/hash_tables.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/intents/web_intent_data.h"
+#include "chrome/browser/profiles/profile_keyed_service.h"
 #include "chrome/browser/webdata/web_data_service.h"
 
 // Handles storing and retrieving of web intents in the web database.
 // The registry provides filtering logic to retrieve specific types of intents.
-class WebIntentsRegistry : public WebDataServiceConsumer {
+class WebIntentsRegistry
+    : public WebDataServiceConsumer,
+      public ProfileKeyedService {
  public:
   // Unique identifier for intent queries.
   typedef int QueryID;
@@ -31,14 +34,11 @@
     virtual ~Consumer() {}
   };
 
-  WebIntentsRegistry();
-  virtual ~WebIntentsRegistry();
-
   // Initializes, binds to a valid WebDataService.
   void Initialize(scoped_refptr<WebDataService> wds);
 
   // Registers a web intent provider.
-  void RegisterIntentProvider(const WebIntentData& intent);
+  virtual void RegisterIntentProvider(const WebIntentData& intent);
 
   // Removes a web intent provider from the registry.
   void UnregisterIntentProvider(const WebIntentData& intent);
@@ -47,11 +47,20 @@
   // |consumer| must not be NULL.
   QueryID GetIntentProviders(const string16& action, Consumer* consumer);
 
+ protected:
+  // Make sure that only WebIntentsRegistryFactory can create an instance of
+  // WebIntentsRegistry.
+  friend class WebIntentsRegistryFactory;
+  friend class WebIntentsRegistryTest;
+
+  WebIntentsRegistry();
+  virtual ~WebIntentsRegistry();
+
  private:
    struct IntentsQuery;
 
   // Maps web data requests to intents queries.
-  // Allows OnWebDataServiceRequestDone to forward to appropiate consumer.
+  // Allows OnWebDataServiceRequestDone to forward to appropriate consumer.
   typedef base::hash_map<WebDataService::Handle, IntentsQuery*> QueryMap;
 
   // WebDataServiceConsumer implementation.
diff --git a/chrome/browser/intents/web_intents_registry_factory.cc b/chrome/browser/intents/web_intents_registry_factory.cc
new file mode 100644
index 0000000..7c4af1d2
--- /dev/null
+++ b/chrome/browser/intents/web_intents_registry_factory.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/intents/web_intents_registry.h"
+#include "chrome/browser/intents/web_intents_registry_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_dependency_manager.h"
+
+// static
+WebIntentsRegistry* WebIntentsRegistryFactory::GetForProfile(Profile* profile) {
+  return static_cast<WebIntentsRegistry*>(
+      GetInstance()->GetServiceForProfile(profile, true));
+}
+
+WebIntentsRegistryFactory::WebIntentsRegistryFactory()
+    : ProfileKeyedServiceFactory(ProfileDependencyManager::GetInstance()) {
+  // TODO(erg): For Shutdown() order, we need to:
+  //     DependsOn(WebDataServiceFactory::GetInstance());
+}
+
+WebIntentsRegistryFactory::~WebIntentsRegistryFactory() {
+}
+
+// static
+WebIntentsRegistryFactory* WebIntentsRegistryFactory::GetInstance() {
+  return Singleton<WebIntentsRegistryFactory>::get();
+}
+
+ProfileKeyedService* WebIntentsRegistryFactory::BuildServiceInstanceFor(
+    Profile* profile) const {
+  WebIntentsRegistry* registry = new WebIntentsRegistry;
+  registry->Initialize(profile->GetWebDataService(Profile::EXPLICIT_ACCESS));
+  return registry;
+}
+
+bool WebIntentsRegistryFactory::ServiceRedirectedInIncognito() {
+  return false;
+}
diff --git a/chrome/browser/intents/web_intents_registry_factory.h b/chrome/browser/intents/web_intents_registry_factory.h
new file mode 100644
index 0000000..4827097b
--- /dev/null
+++ b/chrome/browser/intents/web_intents_registry_factory.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_INTENTS_WEB_INTENTS_REGISTRY_FACTORY_H_
+#define CHROME_BROWSER_INTENTS_WEB_INTENTS_REGISTRY_FACTORY_H_
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+class Profile;
+class WebIntentsRegistry;
+
+// Singleton that owns all WebIntentsRegistrys and associates them with
+// Profiles. Listens for the Profile's destruction notification and cleans up
+// the associated WebIntentsRegistry.
+class WebIntentsRegistryFactory : public ProfileKeyedServiceFactory {
+ public:
+  // Returns the WebIntentsRegistry that provides intent registration for
+  // |profile|.
+  static WebIntentsRegistry* GetForProfile(Profile* profile);
+
+  // Returns the singleton instance of the WebIntentsRegistryFactory.
+  static WebIntentsRegistryFactory* GetInstance();
+
+ private:
+  friend struct DefaultSingletonTraits<WebIntentsRegistryFactory>;
+
+  WebIntentsRegistryFactory();
+  virtual ~WebIntentsRegistryFactory();
+
+  // ProfileKeyedServiceFactory implementation.
+  virtual ProfileKeyedService* BuildServiceInstanceFor(Profile* profile) const;
+  virtual bool ServiceRedirectedInIncognito();
+
+  DISALLOW_COPY_AND_ASSIGN(WebIntentsRegistryFactory);
+};
+
+#endif  // CHROME_BROWSER_INTENTS_WEB_INTENTS_REGISTRY_FACTORY_H_
diff --git a/chrome/browser/intents/web_intents_registry_unittest.cc b/chrome/browser/intents/web_intents_registry_unittest.cc
index 55ae6aa0..d35164a 100644
--- a/chrome/browser/intents/web_intents_registry_unittest.cc
+++ b/chrome/browser/intents/web_intents_registry_unittest.cc
@@ -22,6 +22,7 @@
     wds_ = new WebDataService();
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     wds_->Init(temp_dir_.path());
+
     registry_.Initialize(wds_);
   }
 
@@ -71,6 +72,7 @@
   intent.service_url = GURL("http://google.com");
   intent.action = ASCIIToUTF16("share");
   intent.type = ASCIIToUTF16("image/*");
+  intent.title = ASCIIToUTF16("Google's Sharing Service");
 
   registry_.RegisterIntentProvider(intent);
 
diff --git a/chrome/browser/profiles/profile_keyed_service_factory.cc b/chrome/browser/profiles/profile_keyed_service_factory.cc
index 7ded5bf..1f334038 100644
--- a/chrome/browser/profiles/profile_keyed_service_factory.cc
+++ b/chrome/browser/profiles/profile_keyed_service_factory.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 
-#include <vector>
+#include <map>
 
 #include "base/memory/singleton.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/themes/theme_service_factory.h b/chrome/browser/themes/theme_service_factory.h
index 924704b..33e903e 100644
--- a/chrome/browser/themes/theme_service_factory.h
+++ b/chrome/browser/themes/theme_service_factory.h
@@ -5,12 +5,9 @@
 #ifndef CHROME_BROWSER_THEMES_THEME_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_THEMES_THEME_SERVICE_FACTORY_H_
 
-#include <map>
-
+#include "base/basictypes.h"
 #include "base/memory/singleton.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
-#include "content/common/notification_observer.h"
-#include "content/common/notification_registrar.h"
 
 class ThemeService;
 class Extension;
@@ -42,6 +39,8 @@
   // ProfileKeyedServiceFactory:
   virtual ProfileKeyedService* BuildServiceInstanceFor(Profile* profile) const;
   virtual bool ServiceRedirectedInIncognito();
+
+  DISALLOW_COPY_AND_ASSIGN(ThemeServiceFactory);
 };
 
 #endif  // CHROME_BROWSER_THEMES_THEME_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc b/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc
index b657e6d..0cc656d 100644
--- a/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc
+++ b/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
 
+#include "base/utf_string_conversions.h"
+
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
 #include "base/utf_string_conversions.h"
@@ -23,6 +25,7 @@
 #include "chrome/browser/google/google_util.h"
 #include "chrome/browser/history/history_tab_helper.h"
 #include "chrome/browser/intents/register_intent_handler_infobar_delegate.h"
+#include "chrome/browser/intents/web_intent_data.h"
 #include "chrome/browser/omnibox_search_hint.h"
 #include "chrome/browser/password_manager/password_manager.h"
 #include "chrome/browser/password_manager_delegate_impl.h"
@@ -604,7 +607,18 @@
   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableWebIntents))
     return;
 
-  AddInfoBar(new RegisterIntentHandlerInfoBarDelegate(tab_contents()));
+  GURL service_url(href);
+  if (!service_url.is_valid()) {
+    const GURL& url = tab_contents()->GetURL();
+    service_url = url.Resolve(href);
+  }
+
+  WebIntentData intent;
+  intent.service_url = service_url;
+  intent.action = action;
+  intent.type = type;
+  intent.title = title;
+  AddInfoBar(new RegisterIntentHandlerInfoBarDelegate(tab_contents(), intent));
 }
 
 void TabContentsWrapper::OnSnapshot(const SkBitmap& bitmap) {
diff --git a/chrome/browser/webdata/autofill_table.cc b/chrome/browser/webdata/autofill_table.cc
index c387d86..0d0591d 100644
--- a/chrome/browser/webdata/autofill_table.cc
+++ b/chrome/browser/webdata/autofill_table.cc
@@ -404,7 +404,7 @@
 
 bool AutofillTable::AddFormFieldValue(const FormField& element,
                                       std::vector<AutofillChange>* changes) {
-  return AddFormFieldValueTime(element, changes, base::Time::Now());
+  return AddFormFieldValueTime(element, changes, Time::Now());
 }
 
 bool AutofillTable::GetFormValuesForElementName(const string16& name,
@@ -457,8 +457,8 @@
 }
 
 bool AutofillTable::RemoveFormElementsAddedBetween(
-    base::Time delete_begin,
-    base::Time delete_end,
+    const Time& delete_begin,
+    const Time& delete_end,
     std::vector<AutofillChange>* changes) {
   DCHECK(changes);
   // Query for the pair_id, name, and value of all form elements that
@@ -509,8 +509,8 @@
 }
 
 bool AutofillTable::RemoveFormElementForTimeRange(int64 pair_id,
-                                                  const Time delete_begin,
-                                                  const Time delete_end,
+                                                  const Time& delete_begin,
+                                                  const Time& delete_end,
                                                   int* how_many) {
   sql::Statement s(db_->GetUniqueStatement(
       "DELETE FROM autofill_dates WHERE pair_id = ? AND "
@@ -636,7 +636,7 @@
 }
 
 bool AutofillTable::InsertPairIDAndDate(int64 pair_id,
-                                        base::Time date_created) {
+                                        const Time& date_created) {
   sql::Statement s(db_->GetUniqueStatement(
       "INSERT INTO autofill_dates "
       "(pair_id, date_created) VALUES (?, ?)"));
@@ -659,7 +659,7 @@
 bool AutofillTable::AddFormFieldValuesTime(
     const std::vector<FormField>& elements,
     std::vector<AutofillChange>* changes,
-    base::Time time) {
+    Time time) {
   // Only add one new entry for each unique element name.  Use |seen_names| to
   // track this.  Add up to |kMaximumUniqueNames| unique entries per form.
   const size_t kMaximumUniqueNames = 256;
@@ -714,9 +714,9 @@
 
   bool first_entry = true;
   AutofillKey* current_key_ptr = NULL;
-  std::vector<base::Time>* timestamps_ptr = NULL;
+  std::vector<Time>* timestamps_ptr = NULL;
   string16 name, value;
-  base::Time time;
+  Time time;
   while (s.Step()) {
     name = s.ColumnString16(0);
     value = s.ColumnString16(1);
@@ -725,7 +725,7 @@
     if (first_entry) {
       current_key_ptr = new AutofillKey(name, value);
 
-      timestamps_ptr = new std::vector<base::Time>;
+      timestamps_ptr = new std::vector<Time>;
       timestamps_ptr->push_back(time);
 
       first_entry = false;
@@ -740,7 +740,7 @@
         delete timestamps_ptr;
 
         current_key_ptr = new AutofillKey(name, value);
-        timestamps_ptr = new std::vector<base::Time>;
+        timestamps_ptr = new std::vector<Time>;
       }
       timestamps_ptr->push_back(time);
     }
@@ -759,7 +759,7 @@
 
 bool AutofillTable::GetAutofillTimestamps(const string16& name,
                                           const string16& value,
-                                          std::vector<base::Time>* timestamps) {
+                                          std::vector<Time>* timestamps) {
   DCHECK(timestamps);
   sql::Statement s(db_->GetUniqueStatement(
       "SELECT date_created FROM autofill a JOIN "
@@ -842,7 +842,7 @@
 
 bool AutofillTable::AddFormFieldValueTime(const FormField& element,
                                           std::vector<AutofillChange>* changes,
-                                          base::Time time) {
+                                          Time time) {
   int count = 0;
   int64 pair_id;
 
@@ -1243,8 +1243,8 @@
 }
 
 bool AutofillTable::RemoveAutofillProfilesAndCreditCardsModifiedBetween(
-    base::Time delete_begin,
-    base::Time delete_end,
+    const Time& delete_begin,
+    const Time& delete_end,
     std::vector<std::string>* profile_guids,
     std::vector<std::string>* credit_card_guids) {
   DCHECK(delete_end.is_null() || delete_begin < delete_end);
@@ -1367,10 +1367,9 @@
     return false;
   }
   s.BindInt64(0, pair_id);
-  if (s.Run()) {
-    return RemoveFormElementForTimeRange(pair_id, base::Time(), base::Time(),
-                                         NULL);
-  }
+  if (s.Run())
+    return RemoveFormElementForTimeRange(pair_id, Time(), Time(), NULL);
+
   return false;
 }
 
diff --git a/chrome/browser/webdata/autofill_table.h b/chrome/browser/webdata/autofill_table.h
index ded8c01..6418d96 100644
--- a/chrome/browser/webdata/autofill_table.h
+++ b/chrome/browser/webdata/autofill_table.h
@@ -18,6 +18,10 @@
 class AutofillTableTest;
 class CreditCard;
 
+namespace base {
+class Time;
+}
+
 namespace webkit_glue {
 struct FormField;
 }
@@ -142,15 +146,15 @@
   // removes those rows if the count goes to 0.  A list of all changed
   // keys and whether each was updater or removed is returned in the
   // changes out parameter.
-  bool RemoveFormElementsAddedBetween(base::Time delete_begin,
-                                      base::Time delete_end,
+  bool RemoveFormElementsAddedBetween(const base::Time& delete_begin,
+                                      const base::Time& delete_end,
                                       std::vector<AutofillChange>* changes);
 
   // Removes from autofill_dates rows with given pair_id where date_created lies
   // between delte_begin and delte_end.
   bool RemoveFormElementForTimeRange(int64 pair_id,
-                                     base::Time delete_begin,
-                                     base::Time delete_end,
+                                     const base::Time& delete_begin,
+                                     const base::Time& delete_end,
                                      int* how_many);
 
   // Increments the count in the row corresponding to |pair_id| by
@@ -176,7 +180,7 @@
   bool InsertFormElement(const webkit_glue::FormField& element, int64* pair_id);
 
   // Adds a new row to the autofill_dates table.
-  bool InsertPairIDAndDate(int64 pair_id, base::Time date_created);
+  bool InsertPairIDAndDate(int64 pair_id, const base::Time& date_created);
 
   // Removes row from the autofill tables given |pair_id|.
   bool RemoveFormElementForID(int64 pair_id);
@@ -189,8 +193,8 @@
 
   // Retrieves a single entry from the autofill table.
   virtual bool GetAutofillTimestamps(const string16& name,
-                             const string16& value,
-                             std::vector<base::Time>* timestamps);
+                                     const string16& value,
+                                     std::vector<base::Time>* timestamps);
 
   // Replaces existing autofill entries with the entries supplied in
   // the argument.  If the entry does not already exist, it will be
@@ -239,8 +243,8 @@
   // on or after |delete_begin| and strictly before |delete_end|.  Returns lists
   // of deleted guids in |profile_guids| and |credit_card_guids|.
   bool RemoveAutofillProfilesAndCreditCardsModifiedBetween(
-      base::Time delete_begin,
-      base::Time delete_end,
+      const base::Time& delete_begin,
+      const base::Time& delete_end,
       std::vector<std::string>* profile_guids,
       std::vector<std::string>* credit_card_guids);
 
diff --git a/chrome/browser/webdata/token_service_table.h b/chrome/browser/webdata/token_service_table.h
index db80fd66..ebb455a5 100644
--- a/chrome/browser/webdata/token_service_table.h
+++ b/chrome/browser/webdata/token_service_table.h
@@ -6,6 +6,9 @@
 #define CHROME_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_
 #pragma once
 
+#include <map>
+#include <string>
+
 #include "chrome/browser/webdata/web_database_table.h"
 
 class TokenServiceTable : public WebDatabaseTable {
@@ -34,5 +37,4 @@
   DISALLOW_COPY_AND_ASSIGN(TokenServiceTable);
 };
 
-
 #endif  // CHROME_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_
diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc
index bc943e01..80d75f1d 100644
--- a/chrome/browser/webdata/web_data_service.cc
+++ b/chrome/browser/webdata/web_data_service.cc
@@ -833,9 +833,7 @@
   InitializeDatabaseIfNecessary();
   if (db_ && !request->IsCancelled()) {
     const WebIntentData& intent = request->arg();
-    db_->GetWebIntentsTable()->RemoveWebIntent(intent.action,
-                                               intent.type,
-                                               intent.service_url);
+    db_->GetWebIntentsTable()->RemoveWebIntent(intent);
     ScheduleCommit();
   }
   request->RequestComplete();
@@ -845,9 +843,7 @@
   InitializeDatabaseIfNecessary();
   if (db_ && !request->IsCancelled()) {
     const WebIntentData& intent = request->arg();
-    db_->GetWebIntentsTable()->SetWebIntent(intent.action,
-                                            intent.type,
-                                            intent.service_url);
+    db_->GetWebIntentsTable()->SetWebIntent(intent);
     ScheduleCommit();
   }
   request->RequestComplete();
diff --git a/chrome/browser/webdata/web_database_table.cc b/chrome/browser/webdata/web_database_table.cc
index 1dada9da..c95358d 100644
--- a/chrome/browser/webdata/web_database_table.cc
+++ b/chrome/browser/webdata/web_database_table.cc
@@ -4,6 +4,11 @@
 
 #include "chrome/browser/webdata/web_database_table.h"
 
+WebDatabaseTable::WebDatabaseTable(
+    sql::Connection* db, sql::MetaTable* meta_table)
+        : db_(db), meta_table_(meta_table) {
+}
+
 WebDatabaseTable::~WebDatabaseTable() {
   db_ = NULL;
   meta_table_ = NULL;
diff --git a/chrome/browser/webdata/web_database_table.h b/chrome/browser/webdata/web_database_table.h
index bfcaaf329..cc92e31b 100644
--- a/chrome/browser/webdata/web_database_table.h
+++ b/chrome/browser/webdata/web_database_table.h
@@ -6,16 +6,18 @@
 #define CHROME_BROWSER_WEBDATA_WEB_DATABASE_TABLE_H_
 #pragma once
 
-#include "sql/connection.h"
-#include "sql/init_status.h"
-#include "sql/meta_table.h"
+#include "base/logging.h"
+
+namespace sql {
+class Connection;
+class MetaTable;
+}
 
 // An abstract base class representing a table within a WebDatabase.
 // Each table should subclass this, adding type-specific methods as needed.
 class WebDatabaseTable {
  protected:
-  WebDatabaseTable(sql::Connection* db, sql::MetaTable* meta_table)
-      : db_(db), meta_table_(meta_table) {}
+  WebDatabaseTable(sql::Connection* db, sql::MetaTable* meta_table);
   virtual ~WebDatabaseTable();
 
   // Attempts to initialize the table and returns true if successful.
diff --git a/chrome/browser/webdata/web_intents_table.cc b/chrome/browser/webdata/web_intents_table.cc
index e3b7bbf..4d59a8f6 100644
--- a/chrome/browser/webdata/web_intents_table.cc
+++ b/chrome/browser/webdata/web_intents_table.cc
@@ -24,6 +24,7 @@
                     "service_url LONGVARCHAR,"
                     "action VARCHAR,"
                     "type VARCHAR,"
+                    "title VARCHAR,"
                     "UNIQUE (service_url, action, type))")) {
     NOTREACHED();
     return false;
@@ -68,26 +69,26 @@
   return true;
 }
 
-bool WebIntentsTable::SetWebIntent(const string16& action,
-                                   const string16& type,
-                                   const GURL& service_url) {
+bool WebIntentsTable::SetWebIntent(const WebIntentData& intent) {
   sql::Statement s(db_->GetUniqueStatement(
-      "INSERT OR REPLACE INTO web_intents (service_url, type, action) "
-      "VALUES (?, ?, ?)"));
+      "INSERT OR REPLACE INTO web_intents (service_url, type, action, title) "
+      "VALUES (?, ?, ?, ?)"));
   if (!s) {
     NOTREACHED() << "Statement prepare failed";
     return false;
   }
 
-  s.BindString(0, service_url.spec());
-  s.BindString16(1, type);
-  s.BindString16(2, action);
+  s.BindString(0, intent.service_url.spec());
+  s.BindString16(1, intent.type);
+  s.BindString16(2, intent.action);
+  s.BindString16(3, intent.title);
   return s.Run();
 }
 
-bool WebIntentsTable::RemoveWebIntent(const string16& action,
-                                      const string16& type,
-                                      const GURL& service_url) {
+// TODO(jhawkins): Investigate the need to remove rows matching only
+// |intent.service_url|. It's unlikely the user will be given the ability to
+// remove at the granularity of actions or types.
+bool WebIntentsTable::RemoveWebIntent(const WebIntentData& intent) {
   sql::Statement delete_s(db_->GetUniqueStatement(
       "DELETE FROM web_intents "
       "WHERE service_url = ? AND action = ? AND type = ?"));
@@ -96,9 +97,9 @@
     return false;
   }
 
-  delete_s.BindString(0, service_url.spec());
-  delete_s.BindString16(1, action);
-  delete_s.BindString16(2, type);
+  delete_s.BindString(0, intent.service_url.spec());
+  delete_s.BindString16(1, intent.action);
+  delete_s.BindString16(2, intent.type);
   return delete_s.Run();
 }
 
diff --git a/chrome/browser/webdata/web_intents_table.h b/chrome/browser/webdata/web_intents_table.h
index 5ebf1519..7142fb9 100644
--- a/chrome/browser/webdata/web_intents_table.h
+++ b/chrome/browser/webdata/web_intents_table.h
@@ -15,6 +15,11 @@
 
 class GURL;
 
+namespace sql {
+class Connection;
+class MetaTable;
+}
+
 // This class manages the WebIntents table within the SQLite database passed
 // to the constructor. It expects the following schema:
 //
@@ -35,18 +40,14 @@
 
   // Adds a web intent to the WebIntents table. If intent already exists,
   // replaces it.
-  bool SetWebIntent(const string16& action,
-                    const string16& type,
-                    const GURL& service_url);
+  bool SetWebIntent(const WebIntentData& intent);
 
   // Retrieve all intents from WebIntents table that match |action|.
   bool GetWebIntents(const string16& action,
                      std::vector<WebIntentData>* intents);
 
   // Removes intent from WebIntents table - must match all parameters exactly.
-  bool RemoveWebIntent(const string16& action,
-                       const string16& type,
-                       const GURL& service_url);
+  bool RemoveWebIntent(const WebIntentData& intent);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WebIntentsTable);
diff --git a/chrome/browser/webdata/web_intents_table_unittest.cc b/chrome/browser/webdata/web_intents_table_unittest.cc
index d965138..f6367bc 100644
--- a/chrome/browser/webdata/web_intents_table_unittest.cc
+++ b/chrome/browser/webdata/web_intents_table_unittest.cc
@@ -14,8 +14,6 @@
 #include "googleurl/src/gurl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::Time;
-
 namespace {
 
 GURL test_url("http://google.com/");
@@ -48,19 +46,19 @@
   EXPECT_EQ(0U, intents.size());
 
   // Now adding one.
-  EXPECT_TRUE(IntentsTable()->SetWebIntent(test_action, mime_image, test_url));
+  WebIntentData intent;
+  intent.service_url = test_url;
+  intent.action = test_action;
+  intent.type = mime_image;
+  EXPECT_TRUE(IntentsTable()->SetWebIntent(intent));
 
   // Make sure that intent can now be fetched
   EXPECT_TRUE(IntentsTable()->GetWebIntents(test_action, &intents));
   ASSERT_EQ(1U, intents.size());
-
-  EXPECT_EQ(test_url.spec(), intents[0].service_url.spec());
-  EXPECT_EQ(test_action, intents[0].action);
-  EXPECT_EQ(mime_image, intents[0].type);
+  EXPECT_EQ(intent, intents[0]);
 
   // Remove the intent.
-  EXPECT_TRUE(IntentsTable()->RemoveWebIntent(test_action, mime_image,
-      test_url));
+  EXPECT_TRUE(IntentsTable()->RemoveWebIntent(intent));
 
   // Intent should now be gone.
   intents.clear();
@@ -72,8 +70,14 @@
 TEST_F(WebIntentsTableTest, SetMultipleIntents) {
   std::vector<WebIntentData> intents;
 
-  EXPECT_TRUE(IntentsTable()->SetWebIntent(test_action, mime_image, test_url));
-  EXPECT_TRUE(IntentsTable()->SetWebIntent(test_action, mime_video, test_url));
+  WebIntentData intent;
+  intent.service_url = test_url;
+  intent.action = test_action;
+  intent.type = mime_image;
+  EXPECT_TRUE(IntentsTable()->SetWebIntent(intent));
+
+  intent.type = mime_video;
+  EXPECT_TRUE(IntentsTable()->SetWebIntent(intent));
 
   // Recover stored intents from DB.
   EXPECT_TRUE(IntentsTable()->GetWebIntents(test_action, &intents));
@@ -83,12 +87,9 @@
   if (intents[0].type == mime_video)
     std::swap(intents[0], intents[1]);
 
-  EXPECT_EQ(test_url, intents[0].service_url);
-  EXPECT_EQ(test_action, intents[0].action);
-  EXPECT_EQ(mime_image, intents[0].type);
+  EXPECT_EQ(intent, intents[1]);
 
-  EXPECT_EQ(test_url, intents[1].service_url);
-  EXPECT_EQ(test_action, intents[1].action);
-  EXPECT_EQ(mime_video, intents[1].type);
+  intent.type = mime_image;
+  EXPECT_EQ(intent, intents[0]);
 }
 } // namespace
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 701f3ddc..d3934cd9 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1394,10 +1394,14 @@
         'browser/instant/instant_unload_handler.h',
         'browser/instant/promo_counter.cc',
         'browser/instant/promo_counter.h',
-        'browser/intents/web_intents_registry.cc',
-        'browser/intents/web_intents_registry.h',
         'browser/intents/register_intent_handler_infobar_delegate.cc',
         'browser/intents/register_intent_handler_infobar_delegate.h',
+        'browser/intents/web_intents_registry.cc',
+        'browser/intents/web_intents_registry.h',
+        'browser/intents/web_intents_registry_factory.cc',
+        'browser/intents/web_intents_registry_factory.h',
+        'browser/intents/web_intent_data.cc',
+        'browser/intents/web_intent_data.h',
         'browser/internal_auth.cc',
         'browser/internal_auth.h',
         'browser/intranet_redirect_detector.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 437e2e9c..0206f804 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1479,6 +1479,7 @@
         'browser/importer/toolbar_importer_unittest.cc',
         'browser/instant/instant_loader_manager_unittest.cc',
         'browser/instant/promo_counter_unittest.cc',
+        'browser/intents/register_intent_handler_infobar_delegate_unittest.cc',
         'browser/intents/web_intents_registry_unittest.cc',
         'browser/internal_auth_unittest.cc',
         'browser/language_usage_metrics_unittest.cc',
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index bbaade5..90478f6 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -341,7 +341,6 @@
   // Did the last session exit cleanly? Default is true.
   bool last_session_exited_cleanly_;
 
-
   // FileSystemContext.  Created lazily by GetFileSystemContext().
   scoped_refptr<fileapi::FileSystemContext> file_system_context_;