diff options
author | groby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-30 23:14:27 +0000 |
---|---|---|
committer | groby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-30 23:14:27 +0000 |
commit | 16958d88f010642e90a5bbf74eeeb7b5bc1fea5d (patch) | |
tree | 491742b6a4a6db1f2a8180de13375ae2b46616c5 /chrome | |
parent | 828ba9523d13b9c650c97197bb0189c3c6a31874 (diff) | |
download | chromium_src-16958d88f010642e90a5bbf74eeeb7b5bc1fea5d.zip chromium_src-16958d88f010642e90a5bbf74eeeb7b5bc1fea5d.tar.gz chromium_src-16958d88f010642e90a5bbf74eeeb7b5bc1fea5d.tar.bz2 |
First version of CWS intents query support.
BUG=
TEST=CWSIntentsRegistryTest.*
Review URL: http://codereview.chromium.org/9253024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119759 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/intents/cws_intents_registry.cc | 136 | ||||
-rw-r--r-- | chrome/browser/intents/cws_intents_registry.h | 79 | ||||
-rw-r--r-- | chrome/browser/intents/cws_intents_registry_factory.cc | 39 | ||||
-rw-r--r-- | chrome/browser/intents/cws_intents_registry_factory.h | 42 | ||||
-rw-r--r-- | chrome/browser/intents/cws_intents_registry_unittest.cc | 132 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 4 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 |
7 files changed, 433 insertions, 0 deletions
diff --git a/chrome/browser/intents/cws_intents_registry.cc b/chrome/browser/intents/cws_intents_registry.cc new file mode 100644 index 0000000..040ec2a --- /dev/null +++ b/chrome/browser/intents/cws_intents_registry.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2012 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/cws_intents_registry.h" + +#include "base/callback.h" +#include "base/json/json_value_serializer.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "base/string16.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/net/browser_url_util.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "content/public/common/url_fetcher.h" +#include "net/base/mime_util.h" +#include "net/base/load_flags.h" + +namespace { + +// URL for CWS intents API. TODO(groby): points to staging, fix for M18 release. +const char kCWSIntentServiceURL[] = + "https://www-googleapis-staging.sandbox.google.com" + "/chromewebstore/v1.1b/items/intent"; + +// Build a REST query URL to retrieve intent info from CWS. +GURL BuildQueryURL(const string16& action, const string16& type) { + GURL request(kCWSIntentServiceURL); + request = chrome_browser_net::AppendQueryParameter(request, "intent", + UTF16ToUTF8(action)); + return chrome_browser_net::AppendQueryParameter(request, "mime_types", + UTF16ToUTF8(type)); +} + +} // namespace + +// Internal object representing all data associated with a single query. +struct CWSIntentsRegistry::IntentsQuery { + // Underlying URL request query. + scoped_ptr<content::URLFetcher> url_fetcher_; + + // The callback - invoked on completed retrieval. + ResultsCallback callback_; +}; + +CWSIntentsRegistry::CWSIntentsRegistry(net::URLRequestContextGetter* context) + : request_context_(context) { +} + +CWSIntentsRegistry::~CWSIntentsRegistry() { + // Cancel all pending queries, since we can't handle them any more. + STLDeleteValues(&queries_); +} + +void CWSIntentsRegistry::OnURLFetchComplete(const content::URLFetcher* source) { + DCHECK(source); + + URLFetcherHandle handle = reinterpret_cast<URLFetcherHandle>(source); + QueryMap::iterator it = queries_.find(handle); + DCHECK(it != queries_.end()); + scoped_ptr<IntentsQuery> query(it->second); + DCHECK(query != NULL); + queries_.erase(it); + + std::string response; + source->GetResponseAsString(&response); + + // TODO(groby): Do we really only accept 200, or any 2xx codes? + if (source->GetResponseCode() != 200) + return; + + std::string error; + scoped_ptr<Value> parsed_response; + JSONStringValueSerializer serializer(response); + parsed_response.reset(serializer.Deserialize(NULL, &error)); + if (parsed_response == NULL) + return; + + DictionaryValue* response_dict; + parsed_response->GetAsDictionary(&response_dict); + if (!response_dict) + return; + ListValue* items; + if (!response_dict->GetList("items",&items)) + return; + + IntentExtensionList intents; + for (ListValue::const_iterator iter(items->begin()); + iter != items->end(); ++iter) { + DictionaryValue* item = static_cast<DictionaryValue*>(*iter); + + // All fields are mandatory - skip this result if we can't find a field. + IntentExtensionInfo info; + if (!item->GetInteger("num_ratings", &info.num_ratings)) + continue; + + if (!item->GetDouble("average_rating", &info.average_rating)) + continue; + + if (!item->GetString("manifest", &info.manifest)) + continue; + + string16 url_string; + if (!item->GetString("icon_url", &url_string)) + continue; + info.icon_url = GURL(url_string); + + intents.push_back(info); + } + + if (!query->callback_.is_null()) + query->callback_.Run(intents); +} + +void CWSIntentsRegistry::GetIntentProviders( + const string16& action, const string16& mimetype, + const ResultsCallback& cb) { + scoped_ptr<IntentsQuery> query(new IntentsQuery); + query->callback_ = cb; + query->url_fetcher_.reset(content::URLFetcher::Create( + 0, BuildQueryURL(action,mimetype), content::URLFetcher::GET, this)); + + if (query->url_fetcher_ == NULL) + return; + + query->url_fetcher_->SetRequestContext(request_context_); + query->url_fetcher_->SetLoadFlags( + net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES); + + URLFetcherHandle handle = reinterpret_cast<URLFetcherHandle>( + query->url_fetcher_.get()); + queries_[handle] = query.release(); + queries_[handle]->url_fetcher_->Start(); +} + diff --git a/chrome/browser/intents/cws_intents_registry.h b/chrome/browser/intents/cws_intents_registry.h new file mode 100644 index 0000000..3e3e4db --- /dev/null +++ b/chrome/browser/intents/cws_intents_registry.h @@ -0,0 +1,79 @@ +// Copyright (c) 2012 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_CWS_INTENTS_REGISTRY_H_ +#define CHROME_BROWSER_INTENTS_CWS_INTENTS_REGISTRY_H_ +#pragma once + +#include "base/callback_forward.h" +#include "base/gtest_prod_util.h" +#include "base/hash_tables.h" +#include "base/memory/ref_counted.h" +#include "chrome/browser/profiles/profile_keyed_service.h" +#include "content/public/common/url_fetcher_delegate.h" +#include "googleurl/src/gurl.h" + +namespace net { +class URLRequestContextGetter; +} + +// Handles storing and retrieving of web intents in the web database. +// The registry provides filtering logic to retrieve specific types of intents. +class CWSIntentsRegistry : public ProfileKeyedService, + public content::URLFetcherDelegate { + public: + // Data returned from CWS for a single intent. + struct IntentExtensionInfo { + int num_ratings; // Number of ratings in CWS store. + double average_rating; // The average CWS rating. + string16 manifest; // The containing extension's manifest info. + GURL icon_url; // Where to retrieve an icon for this intent. + }; + + // List of Intent extensions, as returned by GetIntentProviders's |callback| + typedef std::vector<IntentExtensionInfo> IntentExtensionList; + // Callback to return results from GetIntentProviders upon completion. + typedef base::Callback<void(const IntentExtensionList&)> ResultsCallback; + + // Requests all intent providers matching |action| and |mimetype|. + // |mimetype| must conform to definition as outlined for + // WebIntentsRegistry::GetIntentProviders. + // |callback| will be invoked upon retrieving results from CWS, returning + // a list of matching Intent extensions. + void GetIntentProviders(const string16& action, + const string16& mimetype, + const ResultsCallback& callback); + + private: + // Make sure that only CWSIntentsRegistryFactory can create an instance of + // CWSIntentsRegistry. + friend class CWSIntentsRegistryFactory; + FRIEND_TEST_ALL_PREFIXES(CWSIntentsRegistryTest, ValidQuery); + FRIEND_TEST_ALL_PREFIXES(CWSIntentsRegistryTest, InvalidQuery); + + // content::URLFetcherDelegate implementation. + virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE; + + // |context| is a profile-dependent URL request context. Must not be NULL. + explicit CWSIntentsRegistry(net::URLRequestContextGetter* context); + virtual ~CWSIntentsRegistry(); + + struct IntentsQuery; + + // This is an opaque version of URLFetcher*, so we can use it as a hash key. + typedef intptr_t URLFetcherHandle; + + // Maps URL fetchers to queries. + typedef base::hash_map<URLFetcherHandle, IntentsQuery*> QueryMap; + + // Map for all in-flight web data requests/intent queries. + QueryMap queries_; + + // Request context for any CWS requests. + scoped_refptr<net::URLRequestContextGetter> request_context_; + + DISALLOW_COPY_AND_ASSIGN(CWSIntentsRegistry); +}; + +#endif // CHROME_BROWSER_INTENTS_CWS_INTENTS_REGISTRY_H_ diff --git a/chrome/browser/intents/cws_intents_registry_factory.cc b/chrome/browser/intents/cws_intents_registry_factory.cc new file mode 100644 index 0000000..b833521 --- /dev/null +++ b/chrome/browser/intents/cws_intents_registry_factory.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2012 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/cws_intents_registry.h" +#include "chrome/browser/intents/cws_intents_registry_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_dependency_manager.h" + +// static +CWSIntentsRegistry* CWSIntentsRegistryFactory::GetForProfile(Profile* profile) { + return static_cast<CWSIntentsRegistry*>( + GetInstance()->GetServiceForProfile(profile, true)); +} + +CWSIntentsRegistryFactory::CWSIntentsRegistryFactory() + : ProfileKeyedServiceFactory("CWSIntentsRegistry", + ProfileDependencyManager::GetInstance()) { +} + +CWSIntentsRegistryFactory::~CWSIntentsRegistryFactory() { +} + +// static +CWSIntentsRegistryFactory* CWSIntentsRegistryFactory::GetInstance() { + return Singleton<CWSIntentsRegistryFactory>::get(); +} + +ProfileKeyedService* CWSIntentsRegistryFactory::BuildServiceInstanceFor( + Profile* profile) const { + CWSIntentsRegistry* registry = new CWSIntentsRegistry( + profile->GetRequestContext()); + return registry; +} + +bool CWSIntentsRegistryFactory::ServiceRedirectedInIncognito() { + // TODO(groby): Do we have CWS access in incognito? + return false; +} diff --git a/chrome/browser/intents/cws_intents_registry_factory.h b/chrome/browser/intents/cws_intents_registry_factory.h new file mode 100644 index 0000000..28bff47 --- /dev/null +++ b/chrome/browser/intents/cws_intents_registry_factory.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012 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_CWS_INTENTS_REGISTRY_FACTORY_H_ +#define CHROME_BROWSER_INTENTS_CWS_INTENTS_REGISTRY_FACTORY_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/singleton.h" +#include "chrome/browser/profiles/profile_keyed_service_factory.h" + +class Profile; +class CWSIntentsRegistry; + +// Singleton that owns all CWSIntentsRegistrys and associates each with +// their respective profile. Listens for the profile's destruction notification +// and cleans up the associated CWSIntentsRegistry. +class CWSIntentsRegistryFactory : public ProfileKeyedServiceFactory { + public: + // Returns a weak pointer to the WebIntentsRegistry that provides intent + // registration for |profile|. + static CWSIntentsRegistry* GetForProfile(Profile* profile); + + // Returns the singleton instance of the WebIntentsRegistryFactory. + static CWSIntentsRegistryFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<CWSIntentsRegistryFactory>; + + CWSIntentsRegistryFactory(); + virtual ~CWSIntentsRegistryFactory(); + + // ProfileKeyedServiceFactory implementation. + virtual ProfileKeyedService* BuildServiceInstanceFor( + Profile* profile) const OVERRIDE; + virtual bool ServiceRedirectedInIncognito() OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(CWSIntentsRegistryFactory); +}; + +#endif // CHROME_BROWSER_INTENTS_CWS_INTENTS_REGISTRY_FACTORY_H_ diff --git a/chrome/browser/intents/cws_intents_registry_unittest.cc b/chrome/browser/intents/cws_intents_registry_unittest.cc new file mode 100644 index 0000000..40ff63d --- /dev/null +++ b/chrome/browser/intents/cws_intents_registry_unittest.cc @@ -0,0 +1,132 @@ +// Copyright (c) 2012 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/cws_intents_registry.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/message_loop.h" +#include "base/utf_string_conversions.h" +#include "chrome/test/base/test_url_request_context_getter.h" +#include "content/test/test_url_fetcher_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kCWSQueryInvalid[] = + "https://www-googleapis-staging.sandbox.google.com" + "/chromewebstore/v1.1b/items/intent" + "?intent=foo&mime_types=foo"; +const char kCWSResponseInvalid[] = + "{\"error\":{\"errors\":[{\"domain\":\"global\",\"reason\":\"invalid\"," + "\"message\":\"Invalid mimetype:foo\"}],\"code\":400," + "\"message\":\"Invalid mimetype:foo\"}}\""; + +const char kCWSQueryValid[] = + "https://www-googleapis-staging.sandbox.google.com" + "/chromewebstore/v1.1b/items/intent" + "?intent=http%3A%2F%2Fwebintents.org%2Fedit&mime_types=*%2Fpng"; +const char kCWSResponseValid[] = + "{\"kind\":\"chromewebstore#itemList\",\"total_items\":1," + "\"start_index\":0,\"items\":[{\"kind\":\"chromewebstore#item\"," + "\"id\":\"nhkckhebbbncbkefhcpcgepcgfaclehe\",\"type\":\"APPLICATION\"," + "\"num_ratings\":0,\"average_rating\":0.0,\"manifest\":" + "\"{\\n\\\"update_url\\\":\\" + "\"http://0.tbhome_staging.dserver.download-qa.td.borg.google.com/" + "service/update2/crx\\\",\\n \\\"name\\\": \\\"Sidd's Intent App\\\"" + ",\\n \\\"description\\\": \\\"Do stuff\\\",\\n \\\"version\\\": " + "\\\"1.2.19\\\",\\n \\\"app\\\": {\\n \\\"urls\\\": [ \\n ]," + "\\n \\\"launch\\\": {\\n \\\"web_url\\\": \\" + "\"http://siddharthasaha.net/\\\"\\n }\\n },\\n \\\"icons\\\": " + "{\\n \\\"128\\\": \\\"icon128.png\\\"\\n },\\n \\\"permissions\\\":" + " [\\n \\\"unlimitedStorage\\\",\\n \\\"notifications\\\"\\n ],\\n" + " \\\"intents\\\": {\\n \\\"http://webintents.org/edit\\\" : {\\n " + "\\\"type\\\" : [\\\"image/png\\\", \\\"image/jpg\\\"],\\n \\\"path\\" + "\" : \\\"//services/edit\\\",\\n \\\"title\\\" : " + "\\\"Sample Editing Intent\\\",\\n \\\"disposition\\\" : \\\"inline\\" + "\"\\n },\\n \\\"http://webintents.org/share\\\" : " + "{\\n \\\"type\\\" : [\\\"text/plain\\\", \\\"image/jpg\\\"]," + "\\n \\\"path\\\" : \\\"//services/share\\\",\\n \\\"title\\\" : " + "\\\"Sample sharing Intent\\\",\\n \\\"disposition\\\" : " + "\\\"inline\\\"\\n }\\n }\\n}\\n\",\"family_safe\":true,\"icon_url\":" + "\"http://qa-lighthouse.sandbox.google.com/image/" + "QzPnRCYCBbBGI99ZkGxkp-NNJ488IkkiTyCgynFEeDTJHcw4tHl3csmjTQ\"}]}"; +const char kValidIconURL[]= + "http://qa-lighthouse.sandbox.google.com/image/" + "QzPnRCYCBbBGI99ZkGxkp-NNJ488IkkiTyCgynFEeDTJHcw4tHl3csmjTQ"; +const char kValidManifest[]= + "{\n\"update_url\":\"http://0.tbhome_staging.dserver.download-qa.td.borg." + "google.com/service/update2/crx\",\n \"name\": \"Sidd's Intent App\",\n" + " \"description\": \"Do stuff\",\n \"version\": \"1.2.19\",\n \"app\":" + " {\n \"urls\": [ \n ],\n \"launch\": {\n \"web_url\":" + " \"http://siddharthasaha.net/\"\n }\n },\n \"icons\": {\n " + "\"128\": \"icon128.png\"\n },\n \"permissions\": [\n " + "\"unlimitedStorage\",\n \"notifications\"\n ],\n \"intents\": " + "{\n \"http://webintents.org/edit\" : {\n \"type\" : [" + "\"image/png\", \"image/jpg\"],\n \"path\" : \"//services/edit\",\n" + " \"title\" : \"Sample Editing Intent\",\n \"disposition\" : " + "\"inline\"\n },\n \"http://webintents.org/share\" : {\n " + "\"type\" : [\"text/plain\", \"image/jpg\"],\n \"path\" : " + "\"//services/share\",\n \"title\" : \"Sample sharing Intent\",\n" + " \"disposition\" : \"inline\"\n }\n }\n}\n"; + +class CWSIntentsRegistryTest : public testing::Test { + public: + CWSIntentsRegistryTest() : test_factory_(NULL) {} + virtual void TearDown() { + // Pump messages posted by the main thread. + ui_loop_.RunAllPending(); + } + + CWSIntentsRegistry::IntentExtensionList WaitForResults() { + ui_loop_.RunAllPending(); + return extensions_; + } + + void Callback(const CWSIntentsRegistry::IntentExtensionList& extensions) { + extensions_ = extensions; + } + + CWSIntentsRegistry::IntentExtensionList extensions_; + FakeURLFetcherFactory test_factory_; + + private: + MessageLoop ui_loop_; +}; + +} // namespace + +TEST_F(CWSIntentsRegistryTest, ValidQuery) { + TestURLRequestContextGetter context_getter; + test_factory_.SetFakeResponse(kCWSQueryValid, kCWSResponseValid, true); + + CWSIntentsRegistry registry(&context_getter); + registry.GetIntentProviders(ASCIIToUTF16("http://webintents.org/edit"), + ASCIIToUTF16("*/png"), + base::Bind(&CWSIntentsRegistryTest::Callback, + base::Unretained(this))); + + WaitForResults(); + ASSERT_EQ(1UL, extensions_.size()); + + EXPECT_EQ(0, extensions_[0].num_ratings); + EXPECT_EQ(0.0, extensions_[0].average_rating); + EXPECT_EQ(std::string(kValidManifest), UTF16ToUTF8(extensions_[0].manifest)); + EXPECT_EQ(std::string(kValidIconURL), extensions_[0].icon_url.spec()); +} + +TEST_F(CWSIntentsRegistryTest, InvalidQuery) { + TestURLRequestContextGetter context_getter; + test_factory_.SetFakeResponse(kCWSQueryInvalid, kCWSResponseInvalid, true); + + CWSIntentsRegistry registry(&context_getter); + registry.GetIntentProviders(ASCIIToUTF16("foo"), + ASCIIToUTF16("foo"), + base::Bind(&CWSIntentsRegistryTest::Callback, + base::Unretained(this))); + + WaitForResults(); + EXPECT_EQ(0UL, extensions_.size()); +} + diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index de2238b..de91944 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1550,6 +1550,10 @@ 'browser/instant/instant_unload_handler.h', 'browser/instant/promo_counter.cc', 'browser/instant/promo_counter.h', + 'browser/intents/cws_intents_registry.cc', + 'browser/intents/cws_intents_registry.h', + 'browser/intents/cws_intents_registry_factory.cc', + 'browser/intents/cws_intents_registry_factory.h', 'browser/intents/register_intent_handler_infobar_delegate.cc', 'browser/intents/register_intent_handler_infobar_delegate.h', 'browser/intents/web_intents_registry.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 1549df0..ff1a325 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1507,6 +1507,7 @@ 'browser/importer/safari_importer_unittest.mm', 'browser/importer/toolbar_importer_unittest.cc', 'browser/instant/promo_counter_unittest.cc', + 'browser/intents/cws_intents_registry_unittest.cc', 'browser/intents/register_intent_handler_infobar_delegate_unittest.cc', 'browser/intents/web_intents_registry_unittest.cc', 'browser/internal_auth_unittest.cc', |