1// Copyright (c) 2011 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.h" 6 7#import <AppKit/AppKit.h> 8 9#include <string> 10 11#include "base/command_line.h" 12#include "base/message_loop.h" 13#include "base/task.h" 14#import "chrome/browser/cocoa/keystone_glue.h" 15#include "chrome/browser/first_run/first_run.h" 16#include "chrome/browser/prefs/pref_service.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/browser/tab_contents/confirm_infobar_delegate.h" 19#include "chrome/browser/ui/browser.h" 20#include "chrome/browser/ui/browser_list.h" 21#include "chrome/common/chrome_switches.h" 22#include "chrome/common/pref_names.h" 23#include "content/browser/tab_contents/navigation_controller.h" 24#include "content/browser/tab_contents/tab_contents.h" 25#include "grit/chromium_strings.h" 26#include "grit/generated_resources.h" 27#include "grit/theme_resources.h" 28#include "ui/base/l10n/l10n_util.h" 29#include "ui/base/resource/resource_bundle.h" 30 31class SkBitmap; 32 33namespace { 34 35// KeystonePromotionInfoBarDelegate ------------------------------------------- 36 37class KeystonePromotionInfoBarDelegate : public ConfirmInfoBarDelegate { 38 public: 39 explicit KeystonePromotionInfoBarDelegate(TabContents* tab_contents); 40 41 private: 42 virtual ~KeystonePromotionInfoBarDelegate(); 43 44 // Sets this info bar to be able to expire. Called a predetermined amount 45 // of time after this object is created. 46 void SetCanExpire() { can_expire_ = true; } 47 48 // ConfirmInfoBarDelegate 49 virtual bool ShouldExpire( 50 const NavigationController::LoadCommittedDetails& details) const; 51 virtual void InfoBarClosed(); 52 virtual SkBitmap* GetIcon() const; 53 virtual string16 GetMessageText() const; 54 virtual string16 GetButtonLabel(InfoBarButton button) const; 55 virtual bool Accept(); 56 virtual bool Cancel(); 57 58 // The TabContents' profile. 59 Profile* profile_; // weak 60 61 // Whether the info bar should be dismissed on the next navigation. 62 bool can_expire_; 63 64 // Used to delay the expiration of the info bar. 65 ScopedRunnableMethodFactory<KeystonePromotionInfoBarDelegate> method_factory_; 66 67 DISALLOW_COPY_AND_ASSIGN(KeystonePromotionInfoBarDelegate); 68}; 69 70KeystonePromotionInfoBarDelegate::KeystonePromotionInfoBarDelegate( 71 TabContents* tab_contents) 72 : ConfirmInfoBarDelegate(tab_contents), 73 profile_(tab_contents->profile()), 74 can_expire_(false), 75 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 76 const int kCanExpireOnNavigationAfterMilliseconds = 8 * 1000; 77 MessageLoop::current()->PostDelayedTask(FROM_HERE, 78 method_factory_.NewRunnableMethod( 79 &KeystonePromotionInfoBarDelegate::SetCanExpire), 80 kCanExpireOnNavigationAfterMilliseconds); 81} 82 83KeystonePromotionInfoBarDelegate::~KeystonePromotionInfoBarDelegate() { 84} 85 86bool KeystonePromotionInfoBarDelegate::ShouldExpire( 87 const NavigationController::LoadCommittedDetails& details) const { 88 return can_expire_; 89} 90 91void KeystonePromotionInfoBarDelegate::InfoBarClosed() { 92 delete this; 93} 94 95SkBitmap* KeystonePromotionInfoBarDelegate::GetIcon() const { 96 return ResourceBundle::GetSharedInstance().GetBitmapNamed( 97 IDR_PRODUCT_ICON_32); 98} 99 100string16 KeystonePromotionInfoBarDelegate::GetMessageText() const { 101 return l10n_util::GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT, 102 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); 103} 104 105string16 KeystonePromotionInfoBarDelegate::GetButtonLabel( 106 InfoBarButton button) const { 107 return l10n_util::GetStringUTF16((button == BUTTON_OK) ? 108 IDS_PROMOTE_INFOBAR_PROMOTE_BUTTON : IDS_PROMOTE_INFOBAR_DONT_ASK_BUTTON); 109} 110 111bool KeystonePromotionInfoBarDelegate::Accept() { 112 [[KeystoneGlue defaultKeystoneGlue] promoteTicket]; 113 return true; 114} 115 116bool KeystonePromotionInfoBarDelegate::Cancel() { 117 profile_->GetPrefs()->SetBoolean(prefs::kShowUpdatePromotionInfoBar, false); 118 return true; 119} 120 121} // namespace 122 123 124// KeystonePromotionInfoBar --------------------------------------------------- 125 126@interface KeystonePromotionInfoBar : NSObject 127- (void)checkAndShowInfoBarForProfile:(Profile*)profile; 128- (void)updateStatus:(NSNotification*)notification; 129- (void)removeObserver; 130@end // @interface KeystonePromotionInfoBar 131 132@implementation KeystonePromotionInfoBar 133 134- (void)dealloc { 135 [self removeObserver]; 136 [super dealloc]; 137} 138 139- (void)checkAndShowInfoBarForProfile:(Profile*)profile { 140 // If this is the first run, the user clicked the "don't ask again" button 141 // at some point in the past, or if the "don't ask about the default 142 // browser" command-line switch is present, bail out. That command-line 143 // switch is recycled here because it's likely that the set of users that 144 // don't want to be nagged about the default browser also don't want to be 145 // nagged about the update check. (Automated testers, I'm thinking of 146 // you...) 147 CommandLine* commandLine = CommandLine::ForCurrentProcess(); 148 if (FirstRun::IsChromeFirstRun() || 149 !profile->GetPrefs()->GetBoolean(prefs::kShowUpdatePromotionInfoBar) || 150 commandLine->HasSwitch(switches::kNoDefaultBrowserCheck)) { 151 return; 152 } 153 154 // If there is no Keystone glue (maybe because this application isn't 155 // Keystone-enabled) or the application is on a read-only filesystem, 156 // doing anything related to auto-update is pointless. Bail out. 157 KeystoneGlue* keystoneGlue = [KeystoneGlue defaultKeystoneGlue]; 158 if (!keystoneGlue || [keystoneGlue isOnReadOnlyFilesystem]) { 159 return; 160 } 161 162 // Stay alive as long as needed. This is balanced by a release in 163 // -updateStatus:. 164 [self retain]; 165 166 AutoupdateStatus recentStatus = [keystoneGlue recentStatus]; 167 if (recentStatus == kAutoupdateNone || 168 recentStatus == kAutoupdateRegistering) { 169 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 170 [center addObserver:self 171 selector:@selector(updateStatus:) 172 name:kAutoupdateStatusNotification 173 object:nil]; 174 } else { 175 [self updateStatus:[keystoneGlue recentNotification]]; 176 } 177} 178 179- (void)updateStatus:(NSNotification*)notification { 180 NSDictionary* dictionary = [notification userInfo]; 181 AutoupdateStatus status = static_cast<AutoupdateStatus>( 182 [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]); 183 184 if (status == kAutoupdateNone || status == kAutoupdateRegistering) { 185 return; 186 } 187 188 [self removeObserver]; 189 190 if (status != kAutoupdateRegisterFailed && 191 [[KeystoneGlue defaultKeystoneGlue] needsPromotion]) { 192 Browser* browser = BrowserList::GetLastActive(); 193 if (browser) { 194 TabContents* tabContents = browser->GetSelectedTabContents(); 195 196 // Only show if no other info bars are showing, because that's how the 197 // default browser info bar works. 198 if (tabContents && tabContents->infobar_count() == 0) { 199 tabContents->AddInfoBar( 200 new KeystonePromotionInfoBarDelegate(tabContents)); 201 } 202 } 203 } 204 205 [self release]; 206} 207 208- (void)removeObserver { 209 [[NSNotificationCenter defaultCenter] removeObserver:self]; 210} 211 212@end // @implementation KeystonePromotionInfoBar 213 214// static 215void KeystoneInfoBar::PromotionInfoBar(Profile* profile) { 216 KeystonePromotionInfoBar* promotionInfoBar = 217 [[[KeystonePromotionInfoBar alloc] init] autorelease]; 218 219 [promotionInfoBar checkAndShowInfoBarForProfile:profile]; 220} 221