1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/cocoa/keystone_infobar_delegate.h" 6 7 #import <AppKit/AppKit.h> 8 9 #include <string> 10 11 #include "base/bind.h" 12 #include "base/command_line.h" 13 #include "base/memory/weak_ptr.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/prefs/pref_service.h" 16 #include "chrome/browser/first_run/first_run.h" 17 #include "chrome/browser/infobars/confirm_infobar_delegate.h" 18 #include "chrome/browser/infobars/infobar_service.h" 19 #import "chrome/browser/mac/keystone_glue.h" 20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/ui/browser.h" 22 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h" 23 #include "chrome/browser/ui/tabs/tab_strip_model.h" 24 #include "chrome/common/chrome_switches.h" 25 #include "chrome/common/pref_names.h" 26 #include "content/public/browser/navigation_details.h" 27 #include "content/public/browser/web_contents.h" 28 #include "grit/chromium_strings.h" 29 #include "grit/generated_resources.h" 30 #include "grit/theme_resources.h" 31 #include "ui/base/l10n/l10n_util.h" 32 33 class SkBitmap; 34 35 namespace { 36 37 // KeystonePromotionInfoBarDelegate ------------------------------------------- 38 39 class KeystonePromotionInfoBarDelegate : public ConfirmInfoBarDelegate { 40 public: 41 // If there's an active tab, creates a keystone promotion delegate and adds it 42 // to the InfoBarService associated with that tab. 43 static void Create(); 44 45 private: 46 KeystonePromotionInfoBarDelegate(InfoBarService* infobar_service, 47 PrefService* prefs); 48 virtual ~KeystonePromotionInfoBarDelegate(); 49 50 // Sets this info bar to be able to expire. Called a predetermined amount 51 // of time after this object is created. 52 void SetCanExpire() { can_expire_ = true; } 53 54 // ConfirmInfoBarDelegate 55 virtual int GetIconID() const OVERRIDE; 56 virtual string16 GetMessageText() const OVERRIDE; 57 virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; 58 virtual bool Accept() OVERRIDE; 59 virtual bool Cancel() OVERRIDE; 60 virtual bool ShouldExpireInternal( 61 const content::LoadCommittedDetails& details) const OVERRIDE; 62 63 // The prefs to use. 64 PrefService* prefs_; // weak 65 66 // Whether the info bar should be dismissed on the next navigation. 67 bool can_expire_; 68 69 // Used to delay the expiration of the info bar. 70 base::WeakPtrFactory<KeystonePromotionInfoBarDelegate> weak_ptr_factory_; 71 72 DISALLOW_COPY_AND_ASSIGN(KeystonePromotionInfoBarDelegate); 73 }; 74 75 // static 76 void KeystonePromotionInfoBarDelegate::Create() { 77 Browser* browser = chrome::GetLastActiveBrowser(); 78 if (!browser) 79 return; 80 content::WebContents* webContents = 81 browser->tab_strip_model()->GetActiveWebContents(); 82 if (!webContents) 83 return; 84 InfoBarService* infobar_service = 85 InfoBarService::FromWebContents(webContents); 86 infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>( 87 new KeystonePromotionInfoBarDelegate( 88 infobar_service, 89 Profile::FromBrowserContext( 90 webContents->GetBrowserContext())->GetPrefs()))); 91 } 92 93 KeystonePromotionInfoBarDelegate::KeystonePromotionInfoBarDelegate( 94 InfoBarService* infobar_service, 95 PrefService* prefs) 96 : ConfirmInfoBarDelegate(infobar_service), 97 prefs_(prefs), 98 can_expire_(false), 99 weak_ptr_factory_(this) { 100 const base::TimeDelta kCanExpireOnNavigationAfterDelay = 101 base::TimeDelta::FromSeconds(8); 102 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, 103 base::Bind(&KeystonePromotionInfoBarDelegate::SetCanExpire, 104 weak_ptr_factory_.GetWeakPtr()), 105 kCanExpireOnNavigationAfterDelay); 106 } 107 108 KeystonePromotionInfoBarDelegate::~KeystonePromotionInfoBarDelegate() { 109 } 110 111 int KeystonePromotionInfoBarDelegate::GetIconID() const { 112 return IDR_PRODUCT_LOGO_32; 113 } 114 115 string16 KeystonePromotionInfoBarDelegate::GetMessageText() const { 116 return l10n_util::GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT, 117 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); 118 } 119 120 string16 KeystonePromotionInfoBarDelegate::GetButtonLabel( 121 InfoBarButton button) const { 122 return l10n_util::GetStringUTF16((button == BUTTON_OK) ? 123 IDS_PROMOTE_INFOBAR_PROMOTE_BUTTON : IDS_PROMOTE_INFOBAR_DONT_ASK_BUTTON); 124 } 125 126 bool KeystonePromotionInfoBarDelegate::Accept() { 127 [[KeystoneGlue defaultKeystoneGlue] promoteTicket]; 128 return true; 129 } 130 131 bool KeystonePromotionInfoBarDelegate::Cancel() { 132 prefs_->SetBoolean(prefs::kShowUpdatePromotionInfoBar, false); 133 return true; 134 } 135 136 bool KeystonePromotionInfoBarDelegate::ShouldExpireInternal( 137 const content::LoadCommittedDetails& details) const { 138 return can_expire_; 139 } 140 141 } // namespace 142 143 144 // KeystonePromotionInfoBar --------------------------------------------------- 145 146 @interface KeystonePromotionInfoBar : NSObject 147 - (void)checkAndShowInfoBarForProfile:(Profile*)profile; 148 - (void)updateStatus:(NSNotification*)notification; 149 - (void)removeObserver; 150 @end // @interface KeystonePromotionInfoBar 151 152 @implementation KeystonePromotionInfoBar 153 154 - (void)dealloc { 155 [self removeObserver]; 156 [super dealloc]; 157 } 158 159 - (void)checkAndShowInfoBarForProfile:(Profile*)profile { 160 // If this is the first run, the user clicked the "don't ask again" button 161 // at some point in the past, or if the "don't ask about the default 162 // browser" command-line switch is present, bail out. That command-line 163 // switch is recycled here because it's likely that the set of users that 164 // don't want to be nagged about the default browser also don't want to be 165 // nagged about the update check. (Automated testers, I'm thinking of 166 // you...) 167 CommandLine* commandLine = CommandLine::ForCurrentProcess(); 168 if (first_run::IsChromeFirstRun() || 169 !profile->GetPrefs()->GetBoolean(prefs::kShowUpdatePromotionInfoBar) || 170 commandLine->HasSwitch(switches::kNoDefaultBrowserCheck)) { 171 return; 172 } 173 174 // If there is no Keystone glue (maybe because this application isn't 175 // Keystone-enabled) or the application is on a read-only filesystem, 176 // doing anything related to auto-update is pointless. Bail out. 177 KeystoneGlue* keystoneGlue = [KeystoneGlue defaultKeystoneGlue]; 178 if (!keystoneGlue || [keystoneGlue isOnReadOnlyFilesystem]) { 179 return; 180 } 181 182 // Stay alive as long as needed. This is balanced by a release in 183 // -updateStatus:. 184 [self retain]; 185 186 AutoupdateStatus recentStatus = [keystoneGlue recentStatus]; 187 if (recentStatus == kAutoupdateNone || 188 recentStatus == kAutoupdateRegistering) { 189 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 190 [center addObserver:self 191 selector:@selector(updateStatus:) 192 name:kAutoupdateStatusNotification 193 object:nil]; 194 } else { 195 [self updateStatus:[keystoneGlue recentNotification]]; 196 } 197 } 198 199 - (void)updateStatus:(NSNotification*)notification { 200 NSDictionary* dictionary = [notification userInfo]; 201 AutoupdateStatus status = static_cast<AutoupdateStatus>( 202 [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]); 203 204 if (status == kAutoupdateNone || status == kAutoupdateRegistering) { 205 return; 206 } 207 208 [self removeObserver]; 209 210 if (status != kAutoupdateRegisterFailed && 211 [[KeystoneGlue defaultKeystoneGlue] needsPromotion]) { 212 KeystonePromotionInfoBarDelegate::Create(); 213 } 214 215 [self release]; 216 } 217 218 - (void)removeObserver { 219 [[NSNotificationCenter defaultCenter] removeObserver:self]; 220 } 221 222 @end // @implementation KeystonePromotionInfoBar 223 224 // static 225 void KeystoneInfoBar::PromotionInfoBar(Profile* profile) { 226 KeystonePromotionInfoBar* promotionInfoBar = 227 [[[KeystonePromotionInfoBar alloc] init] autorelease]; 228 229 [promotionInfoBar checkAndShowInfoBarForProfile:profile]; 230 } 231