diff options
author | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-24 04:58:52 +0000 |
---|---|---|
committer | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-24 04:58:52 +0000 |
commit | 625d9f8fc428a36e29e6868d3a9bc9f33245d810 (patch) | |
tree | 625bd090aa1b5684ff258ea2799c5b6afb1e1bbe /chrome/browser/ui/intents | |
parent | f8116b6fd136c5c86eaaede373b0363e77e08646 (diff) | |
download | chromium_src-625d9f8fc428a36e29e6868d3a9bc9f33245d810.zip chromium_src-625d9f8fc428a36e29e6868d3a9bc9f33245d810.tar.gz chromium_src-625d9f8fc428a36e29e6868d3a9bc9f33245d810.tar.bz2 |
Web Intent Picker UI (implemented as a constrained dialog, linux only)
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/7648061
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@97990 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/intents')
8 files changed, 811 insertions, 0 deletions
diff --git a/chrome/browser/ui/intents/web_intent_constrained_dialog_factory.cc b/chrome/browser/ui/intents/web_intent_constrained_dialog_factory.cc new file mode 100644 index 0000000..0aa6bbb --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_constrained_dialog_factory.cc @@ -0,0 +1,47 @@ +// 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/ui/intents/web_intent_constrained_dialog_factory.h" + +#include "chrome/browser/ui/intents/web_intent_picker.h" +#include "content/browser/tab_contents/constrained_window.h" +#include "content/browser/tab_contents/tab_contents.h" + +WebIntentConstrainedDialogFactory::WebIntentConstrainedDialogFactory() + : picker_(NULL) { +} + +WebIntentConstrainedDialogFactory::~WebIntentConstrainedDialogFactory() { + CloseDialog(); +} + +WebIntentPicker* WebIntentConstrainedDialogFactory::Create( + TabContents* tab_contents, + WebIntentPickerDelegate* delegate) { + // Only allow one picker per factory. + DCHECK(picker_ == NULL); + + picker_ = WebIntentPicker::Create(tab_contents, delegate); + + // TODO(binji) Remove this check when there are implementations of the picker + // for windows and mac. + if (picker_ == NULL) + return NULL; + + picker_->Show(); + + return picker_; +} + +void WebIntentConstrainedDialogFactory::ClosePicker(WebIntentPicker* picker) { + DCHECK(picker == picker_); + CloseDialog(); +} + +void WebIntentConstrainedDialogFactory::CloseDialog() { + if (picker_) { + picker_->Close(); + picker_ = NULL; + } +} diff --git a/chrome/browser/ui/intents/web_intent_constrained_dialog_factory.h b/chrome/browser/ui/intents/web_intent_constrained_dialog_factory.h new file mode 100644 index 0000000..5efe89f --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_constrained_dialog_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_UI_INTENTS_WEB_INTENT_CONSTRAINED_DIALOG_FACTORY_H_ +#define CHROME_BROWSER_UI_INTENTS_WEB_INTENT_CONSTRAINED_DIALOG_FACTORY_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/ui/intents/web_intent_picker_factory.h" + +class TabContents; +class WebIntentPickerDelegate; + +// A factory for creating a constrained dialog as the intent picker UI for a +// given TabContents. +class WebIntentConstrainedDialogFactory : public WebIntentPickerFactory { + public: + WebIntentConstrainedDialogFactory(); + virtual ~WebIntentConstrainedDialogFactory(); + + // WebIntentPickerFactory implementation. + virtual WebIntentPicker* Create( + TabContents* tab_contents, + WebIntentPickerDelegate* delegate) OVERRIDE; + virtual void ClosePicker(WebIntentPicker* picker) OVERRIDE; + + private: + // Closes the dialog and destroys it. + void CloseDialog(); + + // A weak pointer to the currently active picker, or NULL if there is no + // active picker. + WebIntentPicker* picker_; + + DISALLOW_COPY_AND_ASSIGN(WebIntentConstrainedDialogFactory); +}; + +#endif // CHROME_BROWSER_UI_INTENTS_WEB_INTENT_CONSTRAINED_DIALOG_FACTORY_H_ diff --git a/chrome/browser/ui/intents/web_intent_picker.h b/chrome/browser/ui/intents/web_intent_picker.h new file mode 100644 index 0000000..af2cb19 --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_picker.h @@ -0,0 +1,44 @@ +// 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_UI_INTENTS_WEB_INTENT_PICKER_H_ +#define CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_H_ +#pragma once + +#include <vector> + +class GURL; +class SkBitmap; +class TabContents; +class WebIntentPickerDelegate; + +// Base class for the web intent picker dialog. +class WebIntentPicker { + public: + class Delegate; + + // Platform specific factory function. + static WebIntentPicker* Create(TabContents* tab_contents, + WebIntentPickerDelegate* delegate); + + // Initalizes this picker with the |urls|. + virtual void SetServiceURLs(const std::vector<GURL>& urls) = 0; + + // Sets the icon for a service at |index|. + virtual void SetServiceIcon(size_t index, const SkBitmap& icon) = 0; + + // Sets the icon for a service at |index| to be the default favicon. + virtual void SetDefaultServiceIcon(size_t index) = 0; + + // Shows the UI for this picker. + virtual void Show() = 0; + + // Hides the UI for this picker, and destroys its UI. + virtual void Close() = 0; + + protected: + virtual ~WebIntentPicker() {} +}; + +#endif // CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_H_ diff --git a/chrome/browser/ui/intents/web_intent_picker_controller.cc b/chrome/browser/ui/intents/web_intent_picker_controller.cc new file mode 100644 index 0000000..d348e47 --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_picker_controller.cc @@ -0,0 +1,264 @@ +// 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/ui/intents/web_intent_picker_controller.h" + +#include <vector> + +#include "chrome/browser/favicon/favicon_service.h" +#include "chrome/browser/intents/web_intent_data.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 "chrome/browser/ui/intents/web_intent_picker.h" +#include "chrome/browser/ui/intents/web_intent_picker_factory.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/common/notification_source.h" +#include "ui/gfx/codec/png_codec.h" + +namespace { + +// Gets the favicon service for the profile in |tab_contents|. +FaviconService* GetFaviconService(TabContents* tab_contents) { + Profile* profile = Profile::FromBrowserContext( + tab_contents->browser_context()); + return profile->GetFaviconService(Profile::EXPLICIT_ACCESS); +} + +// Gets the web intents registry for the profile in |tab_contents|. +WebIntentsRegistry* GetWebIntentsRegistry(TabContents* tab_contents) { + Profile* profile = Profile::FromBrowserContext( + tab_contents->browser_context()); + return WebIntentsRegistryFactory::GetForProfile(profile); +} + +} // namespace + +// A class that asynchronously fetches web intent data from the web intents +// registry. +class WebIntentPickerController::WebIntentDataFetcher + : public WebIntentsRegistry::Consumer { + public: + WebIntentDataFetcher(WebIntentPickerController* controller, + WebIntentsRegistry* web_intents_registry); + ~WebIntentDataFetcher(); + + // Asynchronously fetches WebIntentData for the given |action| |type| pair. + void Fetch(const string16& action, const string16& type); + + // Cancels the WebDataService request. + void Cancel(); + + private: + // WebIntentsRegistry::Consumer implementation. + virtual void OnIntentsQueryDone( + WebIntentsRegistry::QueryID, + const std::vector<WebIntentData>& intents) OVERRIDE; + + // A weak pointer to the picker controller. + WebIntentPickerController* controller_; + + // A weak pointer to the web intents registry. + WebIntentsRegistry* web_intents_registry_; + + // The ID of the current web intents request. The value will be -1 if + // there is no request in flight. + WebIntentsRegistry::QueryID query_id_; +}; + +// A class that asynchronously fetches favicons for a vector of URLs. +class WebIntentPickerController::FaviconFetcher { + public: + FaviconFetcher(WebIntentPickerController* controller, + FaviconService *favicon_service); + ~FaviconFetcher(); + + // Asynchronously fetches favicons for the each URL in |urls|. + void Fetch(const std::vector<GURL>& urls); + + // Cancels all favicon requests. + void Cancel(); + + private: + // Callback called when a favicon is received. + void OnFaviconDataAvailable(FaviconService::Handle handle, + history::FaviconData favicon_data); + + // A weak pointer to the picker controller. + WebIntentPickerController* controller_; + + // A weak pointer to the favicon service. + FaviconService* favicon_service_; + + // The Consumer to handle asynchronous favicon requests. + CancelableRequestConsumerTSimple<size_t> load_consumer_; + + DISALLOW_COPY_AND_ASSIGN(FaviconFetcher); +}; + +WebIntentPickerController::WebIntentPickerController( + TabContents* tab_contents, + WebIntentPickerFactory* factory) + : tab_contents_(tab_contents), + picker_factory_(factory), + web_intent_data_fetcher_( + new WebIntentDataFetcher(this, + GetWebIntentsRegistry(tab_contents))), + favicon_fetcher_( + new FaviconFetcher(this, GetFaviconService(tab_contents))), + picker_(NULL), + pending_async_count_(0) { + NavigationController* controller = &tab_contents->controller(); + registrar_.Add(this, content::NOTIFICATION_LOAD_START, + Source<NavigationController>(controller)); + registrar_.Add(this, content::NOTIFICATION_TAB_CLOSING, + Source<NavigationController>(controller)); +} + +WebIntentPickerController::~WebIntentPickerController() { +} + +void WebIntentPickerController::ShowDialog(const string16& action, + const string16& type) { + if (picker_ != NULL) + return; + + picker_ = picker_factory_->Create(tab_contents_, this); + + // TODO(binji) Remove this check when there are implementations of the picker + // for windows and mac. + if (picker_ == NULL) + return; + + web_intent_data_fetcher_->Fetch(action, type); +} + +void WebIntentPickerController::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == content::NOTIFICATION_LOAD_START || + type == content::NOTIFICATION_TAB_CLOSING); + ClosePicker(); +} + +void WebIntentPickerController::OnServiceChosen(size_t index) { + DCHECK(index < urls_.size()); + + // TODO(binji) Send the chosen service back to the renderer. + LOG(INFO) << "Chose index: " << index << " url: " << urls_[index]; + ClosePicker(); +} + +void WebIntentPickerController::OnCancelled() { + // TODO(binji) Tell the renderer that the intent was cancelled. + ClosePicker(); +} + +void WebIntentPickerController::OnWebIntentDataAvailable( + const std::vector<WebIntentData>& intent_data) { + urls_.clear(); + for (size_t i = 0; i < intent_data.size(); ++i) { + urls_.push_back(intent_data[i].service_url); + } + + // Tell the picker to initialize N urls to the default favicon + picker_->SetServiceURLs(urls_); + favicon_fetcher_->Fetch(urls_); + pending_async_count_--; +} + +void WebIntentPickerController::OnFaviconDataAvailable( + size_t index, + const SkBitmap& icon_bitmap) { + picker_->SetServiceIcon(index, icon_bitmap); + pending_async_count_--; +} + +void WebIntentPickerController::OnFaviconDataUnavailable(size_t index) { + picker_->SetDefaultServiceIcon(index); + pending_async_count_--; +} + +void WebIntentPickerController::ClosePicker() { + if (picker_) { + picker_factory_->ClosePicker(picker_); + picker_ = NULL; + } +} + +WebIntentPickerController::WebIntentDataFetcher::WebIntentDataFetcher( + WebIntentPickerController* controller, + WebIntentsRegistry* web_intents_registry) + : controller_(controller), + web_intents_registry_(web_intents_registry), + query_id_(-1) { +} + +WebIntentPickerController::WebIntentDataFetcher::~WebIntentDataFetcher() { +} + +void WebIntentPickerController::WebIntentDataFetcher::Fetch( + const string16& action, + const string16& type) { + DCHECK(query_id_ == -1) << "Fetch already in process."; + controller_->pending_async_count_++; + query_id_ = web_intents_registry_->GetIntentProviders(action, this); +} + +void WebIntentPickerController::WebIntentDataFetcher::OnIntentsQueryDone( + WebIntentsRegistry::QueryID, + const std::vector<WebIntentData>& intents) { + controller_->OnWebIntentDataAvailable(intents); + query_id_ = -1; +} + +WebIntentPickerController::FaviconFetcher::FaviconFetcher( + WebIntentPickerController* controller, + FaviconService* favicon_service) + : controller_(controller), + favicon_service_(favicon_service) { +} + +WebIntentPickerController::FaviconFetcher::~FaviconFetcher() { +} + +void WebIntentPickerController::FaviconFetcher::Fetch( + const std::vector<GURL>& urls) { + if (!favicon_service_) + return; + + for (size_t index = 0; index < urls.size(); ++index) { + controller_->pending_async_count_++; + FaviconService::Handle handle = favicon_service_->GetFaviconForURL( + urls[index], + history::FAVICON, + &load_consumer_, + NewCallback(this, &WebIntentPickerController::FaviconFetcher:: + OnFaviconDataAvailable)); + load_consumer_.SetClientData(favicon_service_, handle, index); + } +} + +void WebIntentPickerController::FaviconFetcher::Cancel() { + load_consumer_.CancelAllRequests(); +} + +void WebIntentPickerController::FaviconFetcher::OnFaviconDataAvailable( + FaviconService::Handle handle, + history::FaviconData favicon_data) { + size_t index = load_consumer_.GetClientDataForCurrentRequest(); + if (favicon_data.is_valid()) { + SkBitmap icon_bitmap; + + if (gfx::PNGCodec::Decode(favicon_data.image_data->front(), + favicon_data.image_data->size(), + &icon_bitmap)) { + controller_->OnFaviconDataAvailable(index, icon_bitmap); + return; + } + } + + controller_->OnFaviconDataUnavailable(index); +} diff --git a/chrome/browser/ui/intents/web_intent_picker_controller.h b/chrome/browser/ui/intents/web_intent_picker_controller.h new file mode 100644 index 0000000..369878c --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_picker_controller.h @@ -0,0 +1,97 @@ +// 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_UI_INTENTS_WEB_INTENT_PICKER_CONTROLLER_H_ +#define CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_CONTROLLER_H_ +#pragma once + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/string16.h" +#include "chrome/browser/ui/intents/web_intent_picker.h" +#include "chrome/browser/ui/intents/web_intent_picker_delegate.h" +#include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" + +class FaviconService; +class GURL; +class SkBitmap; +class TabContents; +class WebDataService; +class WebIntentPickerFactory; +struct WebIntentData; + +// Controls the creation of the WebIntentPicker UI and forwards the user's +// intent handler choice back to the TabContents object. +class WebIntentPickerController : public NotificationObserver, + public WebIntentPickerDelegate { + public: + // Takes ownership of |factory|. + WebIntentPickerController(TabContents* tab_contents, + WebIntentPickerFactory* factory); + virtual ~WebIntentPickerController(); + + // Shows the web intent picker, given the intent |action| and mime-type + // |type|. + void ShowDialog(const string16& action, const string16& type); + + protected: + // NotificationObserver implementation. + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + // WebIntentPickerDelegate implementation. + virtual void OnServiceChosen(size_t index) OVERRIDE; + virtual void OnCancelled() OVERRIDE; + + private: + friend class WebIntentPickerControllerTest; + class WebIntentDataFetcher; + class FaviconFetcher; + + int pending_async_count() const { return pending_async_count_; } + + // Called from the WebIntentDataFetcher when intent data is available. + void OnWebIntentDataAvailable(const std::vector<WebIntentData>& intent_data); + + // Called from the FaviconDataFetcher when a favicon is available. + void OnFaviconDataAvailable(size_t index, const SkBitmap& icon_bitmap); + + // Called from the FaviconDataFetcher when a favicon is not available. + void OnFaviconDataUnavailable(size_t index); + + // Closes the currently active picker. + void ClosePicker(); + + // A weak pointer to the tab contents that the picker is displayed on. + TabContents* tab_contents_; + + // A notification registrar, listening for notifications when the tab closes + // to close the picker ui. + NotificationRegistrar registrar_; + + // A factory to create a new picker. + scoped_ptr<WebIntentPickerFactory> picker_factory_; + + // A helper class to fetch web intent data asynchronously. + scoped_ptr<WebIntentDataFetcher> web_intent_data_fetcher_; + + // A helper class to fetch favicon data asynchronously. + scoped_ptr<FaviconFetcher> favicon_fetcher_; + + // A weak pointer to the picker this controller controls. + WebIntentPicker* picker_; + + // A list of URLs to display in the UI. + std::vector<GURL> urls_; + + // A count of the outstanding asynchronous calls. + int pending_async_count_; + + DISALLOW_COPY_AND_ASSIGN(WebIntentPickerController); +}; + +#endif // CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_CONTROLLER_H_ diff --git a/chrome/browser/ui/intents/web_intent_picker_controller_unittest.cc b/chrome/browser/ui/intents/web_intent_picker_controller_unittest.cc new file mode 100644 index 0000000..89a2e14 --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_picker_controller_unittest.cc @@ -0,0 +1,273 @@ +// 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 "testing/gtest/include/gtest/gtest.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/favicon/favicon_service.h" +#include "chrome/browser/intents/web_intent_data.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 "chrome/browser/ui/intents/web_intent_picker.h" +#include "chrome/browser/ui/intents/web_intent_picker_controller.h" +#include "chrome/browser/ui/intents/web_intent_picker_factory.h" +#include "chrome/browser/ui/tab_contents/test_tab_contents_wrapper.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/test/base/testing_profile.h" +#include "content/browser/browser_thread.h" +#include "content/browser/tab_contents/constrained_window.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/browser/tab_contents/test_tab_contents.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "ui/gfx/codec/png_codec.h" + +using testing::_; +using testing::AtMost; +using testing::DoAll; +using testing::Invoke; +using testing::Return; +using testing::SaveArg; + +namespace { + +const string16 kAction1(ASCIIToUTF16("http://www.example.com/share")); +const string16 kAction2(ASCIIToUTF16("http://www.example.com/foobar")); +const string16 kType(ASCIIToUTF16("image/png")); +const GURL kServiceURL1("http://www.google.com"); +const GURL kServiceURL2("http://www.chromium.org"); +const GURL kServiceURL3("http://www.test.com"); + +// Fill the given bmp with valid png data. +void FillDataToBitmap(int w, int h, SkBitmap* bmp) { + bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h); + bmp->allocPixels(); + + unsigned char* src_data = + reinterpret_cast<unsigned char*>(bmp->getAddr32(0, 0)); + for (int i = 0; i < w * h; i++) { + src_data[i * 4 + 0] = static_cast<unsigned char>(i % 255); + src_data[i * 4 + 1] = static_cast<unsigned char>(i % 255); + src_data[i * 4 + 2] = static_cast<unsigned char>(i % 255); + src_data[i * 4 + 3] = static_cast<unsigned char>(i % 255); + } +} + +// Fill the given data buffer with valid png data. +void FillBitmap(int w, int h, std::vector<unsigned char>* output) { + SkBitmap bitmap; + FillDataToBitmap(w, h, &bitmap); + gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, output); +} + +GURL MakeFaviconURL(const GURL& url) { + return GURL(url.spec() + "/favicon.png"); +} + +MATCHER_P(VectorIsOfSize, n, "") { + return arg.size() == static_cast<size_t>(n); +} + +} // namespace + +class WebIntentPickerMock : public WebIntentPicker { + public: + MOCK_METHOD1(SetServiceURLs, void(const std::vector<GURL>& urls)); + MOCK_METHOD2(SetServiceIcon, void(size_t index, const SkBitmap& icon)); + MOCK_METHOD1(SetDefaultServiceIcon, void(size_t index)); + MOCK_METHOD0(Show, void(void)); + MOCK_METHOD0(Close, void(void)); +}; + +class WebIntentPickerFactoryMock : public WebIntentPickerFactory { + public: + MOCK_METHOD2(Create, + WebIntentPicker*(TabContents* tab_contents, + WebIntentPickerDelegate* delegate)); + MOCK_METHOD1(ClosePicker, void(WebIntentPicker* picker)); +}; + +class TestWebIntentPickerController : public WebIntentPickerController { + public: + TestWebIntentPickerController(TabContents* tab_contents, + WebIntentPickerFactory* factory) + : WebIntentPickerController(tab_contents, factory) { + } + + MOCK_METHOD1(OnServiceChosen, void(size_t index)); + MOCK_METHOD0(OnCancelled, void(void)); + + // helper functions to forward to the base class. + void BaseOnServiceChosen(size_t index) { + WebIntentPickerController::OnServiceChosen(index); + } + + void BaseOnCancelled() { + WebIntentPickerController::OnCancelled(); + } +}; + +class WebIntentPickerControllerTest : public TabContentsWrapperTestHarness { + public: + WebIntentPickerControllerTest() + : ui_thread_(BrowserThread::UI, &message_loop_), + db_thread_(BrowserThread::DB, &message_loop_), + picker_factory_(NULL), + delegate_(NULL) { + } + + virtual void SetUp() { + TabContentsWrapperTestHarness::SetUp(); + + profile_->CreateFaviconService(); + profile_->CreateWebDataService(true); + web_data_service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); + favicon_service_ = profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); + WebIntentsRegistry *registry = + WebIntentsRegistryFactory::GetForProfile(profile_.get()); + registry->Initialize(web_data_service_); + + picker_factory_ = new WebIntentPickerFactoryMock(); + controller_.reset(new TestWebIntentPickerController(contents(), + picker_factory_)); + } + + virtual void TearDown() { + controller_.reset(); + + TabContentsWrapperTestHarness::TearDown(); + } + + protected: + void AddWebIntentService(const string16& action, + const GURL& service_url) { + WebIntentData web_intent_data; + web_intent_data.action = action; + web_intent_data.type = kType; + web_intent_data.service_url = service_url; + web_data_service_->AddWebIntent(web_intent_data); + } + + void AddFaviconForURL(const GURL& url) { + std::vector<unsigned char> image_data; + FillBitmap(16, 16, &image_data); + + favicon_service_->SetFavicon(url, + MakeFaviconURL(url), + image_data, + history::FAVICON); + } + + void SetPickerExpectations(int expected_service_count, + int expected_default_favicons) { + EXPECT_CALL(*picker_factory_, Create(_, _)). + WillOnce(DoAll(SaveArg<1>(&delegate_), Return(&picker_))); + EXPECT_CALL(picker_, + SetServiceURLs(VectorIsOfSize(expected_service_count))). + Times(1); + EXPECT_CALL(picker_, SetDefaultServiceIcon(_)). + Times(expected_default_favicons); + } + + void CheckPendingAsync() { + if (controller_->pending_async_count() > 0) { + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&WebIntentPickerControllerTest::CheckPendingAsync, + base::Unretained(this))); + return; + } + + MessageLoop::current()->Quit(); + } + + void WaitForDialogToShow() { + CheckPendingAsync(); + MessageLoop::current()->Run(); + } + + BrowserThread ui_thread_; + BrowserThread db_thread_; + WebIntentPickerMock picker_; + WebIntentPickerFactoryMock* picker_factory_; // controller_ takes ownership. + scoped_ptr<TestWebIntentPickerController> controller_; + WebIntentPickerDelegate* delegate_; + WebDataService* web_data_service_; + FaviconService* favicon_service_; +}; + +TEST_F(WebIntentPickerControllerTest, ShowDialogWith3Services) { + SetPickerExpectations(3, 3); + AddWebIntentService(kAction1, kServiceURL1); + AddWebIntentService(kAction1, kServiceURL2); + AddWebIntentService(kAction1, kServiceURL3); + + controller_->ShowDialog(kAction1, kType); + WaitForDialogToShow(); +} + +TEST_F(WebIntentPickerControllerTest, ShowDialogWithNoServices) { + SetPickerExpectations(0, 0); + + EXPECT_CALL(picker_, SetServiceIcon(_, _)).Times(0); + + controller_->ShowDialog(kAction1, kType); + WaitForDialogToShow(); +} + +// TODO(binji) SetServiceIcon isn't called unless I create the HistoryService, +// but when I do, the test hangs... +TEST_F(WebIntentPickerControllerTest, DISABLED_ShowFavicon) { + SetPickerExpectations(3, 2); + AddWebIntentService(kAction1, kServiceURL1); + AddWebIntentService(kAction1, kServiceURL2); + AddWebIntentService(kAction1, kServiceURL3); + AddFaviconForURL(kServiceURL1); + AddFaviconForURL(kServiceURL3); + + EXPECT_CALL(picker_, SetServiceIcon(0, _)).Times(1); + EXPECT_CALL(picker_, SetDefaultServiceIcon(1)).Times(1); + EXPECT_CALL(picker_, SetServiceIcon(2, _)).Times(1); + + controller_->ShowDialog(kAction1, kType); + WaitForDialogToShow(); +} + +TEST_F(WebIntentPickerControllerTest, ChooseService) { + SetPickerExpectations(2, 2); + AddWebIntentService(kAction1, kServiceURL1); + AddWebIntentService(kAction1, kServiceURL2); + + EXPECT_CALL(*controller_, OnServiceChosen(0)) + .WillOnce(Invoke(controller_.get(), + &TestWebIntentPickerController::BaseOnServiceChosen)); + EXPECT_CALL(*controller_, OnCancelled()) + .Times(0); + EXPECT_CALL(*picker_factory_, ClosePicker(_)); + + controller_->ShowDialog(kAction1, kType); + WaitForDialogToShow(); + delegate_->OnServiceChosen(0); +} + +TEST_F(WebIntentPickerControllerTest, Cancel) { + SetPickerExpectations(2, 2); + AddWebIntentService(kAction1, kServiceURL1); + AddWebIntentService(kAction1, kServiceURL2); + + EXPECT_CALL(*controller_, OnServiceChosen(0)) + .Times(0); + EXPECT_CALL(*controller_, OnCancelled()) + .WillOnce(Invoke(controller_.get(), + &TestWebIntentPickerController::BaseOnCancelled)); + EXPECT_CALL(*picker_factory_, ClosePicker(_)); + + controller_->ShowDialog(kAction1, kType); + WaitForDialogToShow(); + delegate_->OnCancelled(); +} diff --git a/chrome/browser/ui/intents/web_intent_picker_delegate.h b/chrome/browser/ui/intents/web_intent_picker_delegate.h new file mode 100644 index 0000000..e540e62 --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_picker_delegate.h @@ -0,0 +1,20 @@ +// 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_UI_INTENTS_WEB_INTENT_PICKER_DELEGATE_H_ +#define CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_DELEGATE_H_ +#pragma once + +// A class used to notify the delegate when the user has chosen a web intent +// service. +class WebIntentPickerDelegate { + public: + // Callback called when the user has chosen a service. + virtual void OnServiceChosen(size_t index) = 0; + + // Callback called when the user cancels out of the dialog. + virtual void OnCancelled() = 0; +}; + +#endif // CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_DELEGATE_H_ diff --git a/chrome/browser/ui/intents/web_intent_picker_factory.h b/chrome/browser/ui/intents/web_intent_picker_factory.h new file mode 100644 index 0000000..716bac5 --- /dev/null +++ b/chrome/browser/ui/intents/web_intent_picker_factory.h @@ -0,0 +1,26 @@ +// 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_UI_INTENTS_WEB_INTENT_PICKER_FACTORY_H_ +#define CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_FACTORY_H_ +#pragma once + +class TabContents; +class WebIntentPicker; +class WebIntentPickerDelegate; + +// Interface to create and close the WebIntentPicker UI. +class WebIntentPickerFactory { + public: + virtual ~WebIntentPickerFactory() {} + + // Creates a new WebIntentPicker. The picker is owned by the factory. + virtual WebIntentPicker* Create(TabContents* tab_contents, + WebIntentPickerDelegate* delegate) = 0; + + // Closes and destroys the picker. + virtual void ClosePicker(WebIntentPicker* picker) = 0; +}; + +#endif // CHROME_BROWSER_UI_INTENTS_WEB_INTENT_PICKER_FACTORY_H_ |