Commit a3333996 authored by asargent@chromium.org's avatar asargent@chromium.org

Add code to prompt for browser login during app notification setup

BUG=98145
TEST=Start not signed in to sync, and install an app with the 'experimental'
permission. Then visit a page in the app which calls
chrome.app.experimental.getNotificationChannel({clientId:"foo"}) - you should
get an infobar asking you to sign in.


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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107800 0039d316-1c4b-4281-b951-d872f2087c98
parent 6b26b960
......@@ -8939,6 +8939,17 @@ Keep your key file in a safe place. You will need it to create new versions of y
</message>
</if>
<!-- App Notifications -->
<message name="IDS_APP_NOTIFICATION_NEED_SIGNIN" desc="Displayed when an app wants to send server push notifictions to Chrome, but the user isn't signed in to the browser (which is required for the feature to work).">
<ph name="APP_NAME">$1<ex>Google News</ex></ph> would like to send notifications, but you need to be signed in to Chrome.
</message>
<message name="IDS_APP_NOTIFICATION_NEED_SIGNIN_ACCEPT" desc="Text shown for the button that will take you to sign in to the browser.">
Sign in now
</message>
<message name="IDS_APP_NOTIFICATION_NEED_SIGNIN_CANCEL" desc="Text shown for the button to indicate the user is not interested in the feature.">
No thanks
</message>
<!-- Offline page -->
<if expr="pp_ifdef('chromeos')">
<message name="IDS_OFFLINE_LOAD_HEADLINE" desc="Offline Page HTML headline">
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/app_notify_channel_setup.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
......@@ -20,13 +21,15 @@ AppNotifyChannelSetup::AppNotifyChannelSetup(
const GURL& requestor_url,
int return_route_id,
int callback_id,
AppNotifyChannelUI* ui,
base::WeakPtr<AppNotifyChannelSetup::Delegate> delegate)
: profile_(profile),
client_id_(client_id),
requestor_url_(requestor_url),
return_route_id_(return_route_id),
callback_id_(callback_id),
delegate_(delegate) {}
delegate_(delegate),
ui_(ui) {}
AppNotifyChannelSetup::~AppNotifyChannelSetup() {}
......@@ -52,24 +55,51 @@ static GURL GetChannelServerURL() {
void AppNotifyChannelSetup::Start() {
AddRef(); // Balanced in ReportResult.
GURL channel_server_url = GetChannelServerURL();
// Check if the user is logged in to the browser.
std::string username = profile_->GetPrefs()->GetString(
prefs::kGoogleServicesUsername);
// TODO(asargent) - If the user is not logged in, we'd like to prompt for
// login and if then they sign in, continue as normal. But for now just return
// an error. We do this via PostTask instead of immediately calling back the
if (username.empty()) {
ui_->PromptSyncSetup(this);
return; // We'll get called back in OnSyncSetupResult
}
BeginFetch();
}
void AppNotifyChannelSetup::OnURLFetchComplete(
const content::URLFetcher* source) {
CHECK(source);
net::URLRequestStatus status = source->GetStatus();
if (status.status() == net::URLRequestStatus::SUCCESS &&
source->GetResponseCode() == 200) {
// TODO(asargent) - we need to parse the response from |source| here.
ReportResult("dummy_do_not_use", "");
} else {
ReportResult("", "channel_service_error");
}
}
void AppNotifyChannelSetup::OnSyncSetupResult(bool enabled) {
if (enabled) {
BeginFetch();
} else {
ReportResult("", "not_available");
}
}
void AppNotifyChannelSetup::BeginFetch() {
GURL channel_server_url = GetChannelServerURL();
// We return the error via PostTask instead of immediately calling back the
// delegate because it simplifies tests.
if (!channel_server_url.is_valid() || username.empty()) {
if (!channel_server_url.is_valid()) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
NewRunnableMethod(this,
&AppNotifyChannelSetup::ReportResult,
std::string(),
std::string("not_available")));
base::Bind(&AppNotifyChannelSetup::ReportResult, this,
std::string(), std::string("not_available")));
return;
}
......@@ -85,20 +115,6 @@ void AppNotifyChannelSetup::Start() {
url_fetcher_->Start();
}
void AppNotifyChannelSetup::OnURLFetchComplete(
const content::URLFetcher* source) {
CHECK(source);
net::URLRequestStatus status = source->GetStatus();
if (status.status() == net::URLRequestStatus::SUCCESS &&
source->GetResponseCode() == 200) {
// TODO(asargent) - we need to parse the response from |source| here.
ReportResult("dummy_do_not_use", "");
} else {
ReportResult("", "channel_service_error");
}
}
void AppNotifyChannelSetup::ReportResult(
const std::string& channel_id,
const std::string& error) {
......
......@@ -8,6 +8,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/extensions/app_notify_channel_ui.h"
#include "content/public/common/url_fetcher_delegate.h"
#include "googleurl/src/gurl.h"
......@@ -17,6 +18,7 @@ class Profile;
// app to use when sending server push notifications.
class AppNotifyChannelSetup
: public content::URLFetcherDelegate,
public AppNotifyChannelUI::Delegate,
public base::RefCountedThreadSafe<AppNotifyChannelSetup> {
public:
class Delegate {
......@@ -30,27 +32,32 @@ class AppNotifyChannelSetup
int callback_id) = 0;
};
// Ownership of |ui| is transferred to this object.
AppNotifyChannelSetup(Profile* profile,
const std::string& client_id,
const GURL& requestor_url,
int return_route_id,
int callback_id,
AppNotifyChannelUI* ui,
base::WeakPtr<Delegate> delegate);
// This begins the process of fetching the channel id using the browser login
// credentials. If the user isn't logged in to chrome, this will first cause a
// prompt to appear asking the user to log in.
// credentials (or using |ui_| to prompt for login if needed).
void Start();
protected:
// content::URLFetcherDelegate.
virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE;
// AppNotifyChannelUI::Delegate.
virtual void OnSyncSetupResult(bool enabled) OVERRIDE;
private:
friend class base::RefCountedThreadSafe<AppNotifyChannelSetup>;
virtual ~AppNotifyChannelSetup();
void BeginFetch();
void ReportResult(const std::string& channel_id, const std::string& error);
Profile* profile_;
......@@ -60,6 +67,7 @@ class AppNotifyChannelSetup
int callback_id_;
base::WeakPtr<Delegate> delegate_;
scoped_ptr<content::URLFetcher> url_fetcher_;
scoped_ptr<AppNotifyChannelUI> ui_;
DISALLOW_COPY_AND_ASSIGN(AppNotifyChannelSetup);
};
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
......@@ -64,11 +65,53 @@ class TestDelegate : public AppNotifyChannelSetup::Delegate,
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
class TestUI : public AppNotifyChannelUI {
public:
TestUI() : delegate_(NULL) {}
~TestUI() {}
// AppNotifyChannelUI.
virtual void PromptSyncSetup(Delegate* delegate) OVERRIDE {
CHECK(!delegate_);
delegate_ = delegate;
// If we have a result, post a task to call back the delegate with
// it. Otherwise ReportResult can be called manually at some later point.
if (setup_result_.get()) {
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&TestUI::ReportResult,
base::Unretained(this),
*setup_result_));
}
}
// This will make us automatically call back the delegate with |result| after
// PromptSyncSetup is called.
void SetSyncSetupResult(bool result) {
setup_result_.reset(new bool);
*setup_result_ = result;
}
void ReportResult(bool result) {
CHECK(delegate_);
delegate_->OnSyncSetupResult(result);
}
private:
AppNotifyChannelUI::Delegate* delegate_;
scoped_ptr<bool> setup_result_;
DISALLOW_COPY_AND_ASSIGN(TestUI);
};
} // namespace
class AppNotifyChannelSetupTest : public testing::Test {
public:
AppNotifyChannelSetupTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
AppNotifyChannelSetupTest() : ui_thread_(BrowserThread::UI, &message_loop_),
ui_(new TestUI()) {
}
virtual ~AppNotifyChannelSetupTest() {}
......@@ -103,6 +146,7 @@ class AppNotifyChannelSetupTest : public testing::Test {
page_url,
kRouteId,
kCallbackId,
ui_.release(),
delegate_.AsWeakPtr());
setup->Start();
message_loop_.Run();
......@@ -114,17 +158,20 @@ class AppNotifyChannelSetupTest : public testing::Test {
content::TestBrowserThread ui_thread_;
TestingProfile profile_;
TestDelegate delegate_;
scoped_ptr<TestUI> ui_;
};
TEST_F(AppNotifyChannelSetupTest, NotAvailable) {
TEST_F(AppNotifyChannelSetupTest, DidNotLogInToSync) {
GURL url("http://www.google.com");
ui_->SetSyncSetupResult(false);
scoped_refptr<AppNotifyChannelSetup > setup =
new AppNotifyChannelSetup(&profile_,
"1234",
url,
kRouteId,
kCallbackId,
ui_.release(),
delegate_.AsWeakPtr());
setup->Start();
message_loop_.Run();
......
// 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/extensions/app_notify_channel_ui.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/infobars/infobar_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/sync_setup_wizard.h"
#include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
class AppNotifyChannelUIImpl::InfoBar : public ConfirmInfoBarDelegate {
public:
InfoBar(AppNotifyChannelUIImpl* creator,
InfoBarTabHelper* helper,
const std::string& app_name);
virtual ~InfoBar();
// ConfirmInfoBarDelegate.
virtual string16 GetMessageText() const OVERRIDE;
virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
virtual bool Accept() OVERRIDE;
virtual bool Cancel() OVERRIDE;
virtual void InfoBarDismissed() OVERRIDE;
private:
AppNotifyChannelUIImpl* creator_;
std::string app_name_;
DISALLOW_COPY_AND_ASSIGN(InfoBar);
};
AppNotifyChannelUIImpl::InfoBar::InfoBar(
AppNotifyChannelUIImpl* creator,
InfoBarTabHelper* helper,
const std::string& app_name)
: ConfirmInfoBarDelegate(helper), creator_(creator), app_name_(app_name) {
}
AppNotifyChannelUIImpl::InfoBar::~InfoBar() {}
string16 AppNotifyChannelUIImpl::InfoBar::GetMessageText() const {
return l10n_util::GetStringFUTF16(IDS_APP_NOTIFICATION_NEED_SIGNIN,
UTF8ToUTF16(app_name_));
}
string16 AppNotifyChannelUIImpl::InfoBar::GetButtonLabel(
InfoBarButton button) const {
if (button == BUTTON_OK) {
return l10n_util::GetStringUTF16(IDS_APP_NOTIFICATION_NEED_SIGNIN_ACCEPT);
} else if (button == BUTTON_CANCEL) {
return l10n_util::GetStringUTF16(IDS_APP_NOTIFICATION_NEED_SIGNIN_CANCEL);
} else {
NOTREACHED();
}
return string16();
}
bool AppNotifyChannelUIImpl::InfoBar::Accept() {
creator_->OnInfoBarResult(true);
return true;
}
bool AppNotifyChannelUIImpl::InfoBar::Cancel() {
creator_->OnInfoBarResult(false);
return true;
}
void AppNotifyChannelUIImpl::InfoBar::InfoBarDismissed() {
Cancel();
}
AppNotifyChannelUIImpl::AppNotifyChannelUIImpl(Browser* browser,
TabContentsWrapper* wrapper,
const std::string& app_name)
: browser_(browser), wrapper_(wrapper), app_name_(app_name),
delegate_(NULL), observing_sync_(false), got_first_sync_callback_(false) {
}
AppNotifyChannelUIImpl::~AppNotifyChannelUIImpl() {
// We should have either not started observing sync, or already called
// StopObservingSync by this point.
CHECK(!observing_sync_);
}
void AppNotifyChannelUIImpl::PromptSyncSetup(
AppNotifyChannelUI::Delegate* delegate) {
CHECK(delegate_ == NULL);
delegate_ = delegate;
if (!browser_->profile()->HasProfileSyncService()) {
delegate_->OnSyncSetupResult(false);
return;
}
InfoBarTabHelper* helper = wrapper_->infobar_tab_helper();
helper->AddInfoBar(new AppNotifyChannelUIImpl::InfoBar(
this, helper, app_name_));
}
void AppNotifyChannelUIImpl::OnInfoBarResult(bool accepted) {
if (accepted) {
StartObservingSync();
browser_->ShowSyncSetup();
} else {
delegate_->OnSyncSetupResult(false);
}
}
void AppNotifyChannelUIImpl::OnStateChanged() {
ProfileSyncService* sync_service =
browser_->profile()->GetProfileSyncService();
bool finished = got_first_sync_callback_ && !sync_service->SetupInProgress();
got_first_sync_callback_ = true;
if (finished) {
StopObservingSync();
delegate_->OnSyncSetupResult(sync_service->HasSyncSetupCompleted());
}
}
void AppNotifyChannelUIImpl::StartObservingSync() {
CHECK(!observing_sync_);
observing_sync_ = true;
browser_->profile()->GetProfileSyncService()->AddObserver(this);
}
void AppNotifyChannelUIImpl::StopObservingSync() {
CHECK(observing_sync_);
observing_sync_ = false;
browser_->profile()->GetProfileSyncService()->RemoveObserver(this);
}
// 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_EXTENSIONS_APP_NOTIFY_CHANNEL_UI_H_
#define CHROME_BROWSER_EXTENSIONS_APP_NOTIFY_CHANNEL_UI_H_
#include <string>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "chrome/browser/sync/profile_sync_service_observer.h"
class Browser;
class TabContentsWrapper;
// An interface for prompting a user to sign in to sync so that we can create
// an app notification channel for one of their apps.
class AppNotifyChannelUI {
public:
virtual ~AppNotifyChannelUI() {}
class Delegate {
public:
// A callback for whether the user successfully set up sync or not.
virtual void OnSyncSetupResult(bool enabled) = 0;
};
// Shows a prompt for sync setup - |delegate| will be called back later when
// setup is complete or cancelled. This should only be called once per
// instance.
virtual void PromptSyncSetup(Delegate* delegate) = 0;
};
class AppNotifyChannelUIImpl : public AppNotifyChannelUI,
public ProfileSyncServiceObserver {
public:
AppNotifyChannelUIImpl(Browser* browser,
TabContentsWrapper* wrapper,
const std::string& app_name);
virtual ~AppNotifyChannelUIImpl();
// AppNotifyChannelUI.
virtual void PromptSyncSetup(AppNotifyChannelUI::Delegate* delegate) OVERRIDE;
protected:
// A private class we use to put up an infobar - its lifetime is managed by
// |wrapper_|, so we don't have one as an instance variable.
class InfoBar;
friend class AppNotifyChannelUIImpl::InfoBar;
// Called by our InfoBar when it's accepted or cancelled/closed.
void OnInfoBarResult(bool accepted);
// ProfileSyncServiceObserver.
virtual void OnStateChanged() OVERRIDE;
private:
void StartObservingSync();
void StopObservingSync();
Browser* browser_;
TabContentsWrapper* wrapper_;
std::string app_name_;
AppNotifyChannelUI::Delegate* delegate_;
// Have we registered ourself as a ProfileSyncServiceObserver?
bool observing_sync_;
// This is for working around a bug where the first ProfileSyncServiceObserver
// callback after starting the sync login process erroneously reports
// SetupInProgress as false. See crbug.com/101842.
bool got_first_sync_callback_;
DISALLOW_COPY_AND_ASSIGN(AppNotifyChannelUIImpl);
};
#endif // CHROME_BROWSER_EXTENSIONS_APP_NOTIFY_CHANNEL_UI_H_
......@@ -187,12 +187,16 @@ void ExtensionTabHelper::OnGetAppNotifyChannel(
return;
}
AppNotifyChannelUI* ui = new AppNotifyChannelUIImpl(
GetBrowser(), tab_contents_wrapper(), extension->name());
scoped_refptr<AppNotifyChannelSetup> channel_setup(
new AppNotifyChannelSetup(profile,
client_id,
requestor_url,
return_route_id,
callback_id,
ui,
this->AsWeakPtr()));
channel_setup->Start();
// We'll get called back in AppNotifyChannelSetupComplete.
......
......@@ -946,6 +946,8 @@
'browser/extensions/app_notification.h',
'browser/extensions/app_notification_manager.cc',
'browser/extensions/app_notification_manager.h',
'browser/extensions/app_notify_channel_ui.cc',
'browser/extensions/app_notify_channel_ui.h',
'browser/extensions/app_notify_channel_setup.cc',
'browser/extensions/app_notify_channel_setup.h',
'browser/extensions/app_notification_storage.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment