| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/api/identity/identity_api.h" | 5 #include "chrome/browser/extensions/api/identity/identity_api.h" |
| 6 | 6 |
| 7 #include "base/lazy_instance.h" | 7 #include "base/lazy_instance.h" |
| 8 #include "base/values.h" | 8 #include "base/values.h" |
| 9 #include "chrome/browser/app_mode/app_mode_utils.h" | 9 #include "chrome/browser/app_mode/app_mode_utils.h" |
| 10 #include "chrome/browser/extensions/extension_function_dispatcher.h" | 10 #include "chrome/browser/extensions/extension_function_dispatcher.h" |
| 11 #include "chrome/browser/extensions/extension_install_prompt.h" | 11 #include "chrome/browser/extensions/extension_install_prompt.h" |
| 12 #include "chrome/browser/extensions/extension_service.h" | 12 #include "chrome/browser/extensions/extension_service.h" |
| 13 #include "chrome/browser/extensions/permissions_updater.h" | 13 #include "chrome/browser/extensions/permissions_updater.h" |
| 14 #include "chrome/browser/profiles/profile.h" | 14 #include "chrome/browser/profiles/profile.h" |
| 15 #include "chrome/browser/signin/signin_manager.h" |
| 16 #include "chrome/browser/signin/signin_manager_factory.h" |
| 15 #include "chrome/browser/signin/token_service.h" | 17 #include "chrome/browser/signin/token_service.h" |
| 16 #include "chrome/browser/signin/token_service_factory.h" | 18 #include "chrome/browser/signin/token_service_factory.h" |
| 17 #include "chrome/browser/ui/browser.h" | 19 #include "chrome/browser/ui/browser.h" |
| 18 #include "chrome/browser/ui/browser_navigator.h" | |
| 19 #include "chrome/browser/ui/webui/signin/login_ui_service.h" | |
| 20 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" | |
| 21 #include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h" | |
| 22 #include "chrome/common/extensions/api/experimental_identity.h" | 20 #include "chrome/common/extensions/api/experimental_identity.h" |
| 23 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" | 21 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" |
| 24 #include "chrome/common/extensions/extension.h" | 22 #include "chrome/common/extensions/extension.h" |
| 25 #include "chrome/common/extensions/extension_manifest_constants.h" | 23 #include "chrome/common/extensions/extension_manifest_constants.h" |
| 26 #include "chrome/common/extensions/manifest_handler.h" | 24 #include "chrome/common/extensions/manifest_handler.h" |
| 27 #include "chrome/common/url_constants.h" | 25 #include "chrome/common/url_constants.h" |
| 28 #include "content/public/common/page_transition_types.h" | 26 #include "content/public/common/page_transition_types.h" |
| 27 #include "google_apis/gaia/gaia_constants.h" |
| 29 #include "googleurl/src/gurl.h" | 28 #include "googleurl/src/gurl.h" |
| 30 #include "ui/base/window_open_disposition.h" | 29 #include "ui/base/window_open_disposition.h" |
| 31 | 30 |
| 32 #if defined(OS_CHROMEOS) | 31 #if defined(OS_CHROMEOS) |
| 33 #include "chrome/browser/chromeos/login/user_manager.h" | 32 #include "chrome/browser/chromeos/login/user_manager.h" |
| 34 #endif | 33 #endif |
| 35 | 34 |
| 36 namespace extensions { | 35 namespace extensions { |
| 37 | 36 |
| 38 namespace identity_constants { | 37 namespace identity_constants { |
| 39 const char kInvalidClientId[] = "Invalid OAuth2 Client ID."; | 38 const char kInvalidClientId[] = "Invalid OAuth2 Client ID."; |
| 40 const char kInvalidScopes[] = "Invalid OAuth2 scopes."; | 39 const char kInvalidScopes[] = "Invalid OAuth2 scopes."; |
| 41 const char kAuthFailure[] = "OAuth2 request failed: "; | 40 const char kAuthFailure[] = "OAuth2 request failed: "; |
| 42 const char kNoGrant[] = "OAuth2 not granted or revoked."; | 41 const char kNoGrant[] = "OAuth2 not granted or revoked."; |
| 43 const char kUserRejected[] = "The user did not approve access."; | 42 const char kUserRejected[] = "The user did not approve access."; |
| 44 const char kUserNotSignedIn[] = "The user is not signed in."; | 43 const char kUserNotSignedIn[] = "The user is not signed in."; |
| 45 const char kInvalidRedirect[] = "Did not redirect to the right URL."; | 44 const char kInvalidRedirect[] = "Did not redirect to the right URL."; |
| 46 } // namespace identity_constants | 45 } // namespace identity_constants |
| 47 | 46 |
| 48 namespace GetAuthToken = api::experimental_identity::GetAuthToken; | 47 namespace GetAuthToken = api::experimental_identity::GetAuthToken; |
| 49 namespace LaunchWebAuthFlow = api::experimental_identity::LaunchWebAuthFlow; | 48 namespace LaunchWebAuthFlow = api::experimental_identity::LaunchWebAuthFlow; |
| 50 namespace identity = api::experimental_identity; | 49 namespace identity = api::experimental_identity; |
| 51 | 50 |
| 52 IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() | 51 IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() |
| 53 : interactive_(false) {} | 52 : should_prompt_for_scopes_(false), |
| 53 should_prompt_for_signin_(false) {} |
| 54 IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {} | 54 IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {} |
| 55 | 55 |
| 56 bool IdentityGetAuthTokenFunction::RunImpl() { | 56 bool IdentityGetAuthTokenFunction::RunImpl() { |
| 57 scoped_ptr<GetAuthToken::Params> params(GetAuthToken::Params::Create(*args_)); | 57 scoped_ptr<GetAuthToken::Params> params(GetAuthToken::Params::Create(*args_)); |
| 58 EXTENSION_FUNCTION_VALIDATE(params.get()); | 58 EXTENSION_FUNCTION_VALIDATE(params.get()); |
| 59 if (params->details.get() && params->details->interactive.get()) | 59 bool interactive = params->details.get() && |
| 60 interactive_ = *params->details->interactive; | 60 params->details->interactive.get() && |
| 61 *params->details->interactive; |
| 62 |
| 63 should_prompt_for_scopes_ = interactive; |
| 64 should_prompt_for_signin_ = interactive; |
| 61 | 65 |
| 62 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); | 66 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); |
| 63 | 67 |
| 64 // Check that the necessary information is present in the manfist. | 68 // Check that the necessary information is present in the manifest. |
| 65 if (oauth2_info.client_id.empty()) { | 69 if (oauth2_info.client_id.empty()) { |
| 66 error_ = identity_constants::kInvalidClientId; | 70 error_ = identity_constants::kInvalidClientId; |
| 67 return false; | 71 return false; |
| 68 } | 72 } |
| 69 | 73 |
| 70 if (oauth2_info.scopes.size() == 0) { | 74 if (oauth2_info.scopes.size() == 0) { |
| 71 error_ = identity_constants::kInvalidScopes; | 75 error_ = identity_constants::kInvalidScopes; |
| 72 return false; | 76 return false; |
| 73 } | 77 } |
| 74 | 78 |
| 75 // Balanced in OnIssueAdviceSuccess|OnMintTokenSuccess|OnMintTokenFailure| | 79 // Balanced in OnIssueAdviceSuccess|OnMintTokenSuccess|OnMintTokenFailure| |
| 76 // InstallUIAbort|OnLoginUIClosed. | 80 // InstallUIAbort|SigninFailed. |
| 77 AddRef(); | 81 AddRef(); |
| 78 | 82 |
| 79 if (!HasLoginToken()) { | 83 if (!HasLoginToken()) { |
| 80 if (StartLogin()) { | 84 if (!should_prompt_for_signin_) { |
| 81 return true; | 85 error_ = identity_constants::kUserNotSignedIn; |
| 82 } else { | |
| 83 Release(); | 86 Release(); |
| 84 return false; | 87 return false; |
| 85 } | 88 } |
| 89 // Display a login prompt. If the subsequent mint fails, don't display the |
| 90 // prompt again. |
| 91 should_prompt_for_signin_ = false; |
| 92 ShowLoginPopup(); |
| 93 } else { |
| 94 TokenService* token_service = TokenServiceFactory::GetForProfile(profile()); |
| 95 refresh_token_ = token_service->GetOAuth2LoginRefreshToken(); |
| 96 StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE); |
| 86 } | 97 } |
| 87 | 98 |
| 88 if (StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE)) { | 99 return true; |
| 89 return true; | |
| 90 } else { | |
| 91 Release(); | |
| 92 return false; | |
| 93 } | |
| 94 } | 100 } |
| 95 | 101 |
| 96 void IdentityGetAuthTokenFunction::OnMintTokenSuccess( | 102 void IdentityGetAuthTokenFunction::OnMintTokenSuccess( |
| 97 const std::string& access_token) { | 103 const std::string& access_token) { |
| 98 SetResult(Value::CreateStringValue(access_token)); | 104 SetResult(Value::CreateStringValue(access_token)); |
| 99 SendResponse(true); | 105 SendResponse(true); |
| 100 Release(); // Balanced in RunImpl. | 106 Release(); // Balanced in RunImpl. |
| 101 } | 107 } |
| 102 | 108 |
| 103 void IdentityGetAuthTokenFunction::OnMintTokenFailure( | 109 void IdentityGetAuthTokenFunction::OnMintTokenFailure( |
| 104 const GoogleServiceAuthError& error) { | 110 const GoogleServiceAuthError& error) { |
| 111 switch (error.state()) { |
| 112 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: |
| 113 case GoogleServiceAuthError::ACCOUNT_DELETED: |
| 114 case GoogleServiceAuthError::ACCOUNT_DISABLED: |
| 115 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( |
| 116 profile())->ReportAuthError(error); |
| 117 if (should_prompt_for_signin_) { |
| 118 // Display a login prompt and try again (once). |
| 119 should_prompt_for_signin_ = false; |
| 120 ShowLoginPopup(); |
| 121 return; |
| 122 } |
| 123 break; |
| 124 default: |
| 125 // Return error to caller. |
| 126 break; |
| 127 } |
| 128 |
| 105 error_ = std::string(identity_constants::kAuthFailure) + error.ToString(); | 129 error_ = std::string(identity_constants::kAuthFailure) + error.ToString(); |
| 106 SendResponse(false); | 130 SendResponse(false); |
| 107 Release(); // Balanced in RunImpl. | 131 Release(); // Balanced in RunImpl. |
| 108 } | 132 } |
| 109 | 133 |
| 110 void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( | 134 void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( |
| 111 const IssueAdviceInfo& issue_advice) { | 135 const IssueAdviceInfo& issue_advice) { |
| 136 should_prompt_for_signin_ = false; |
| 112 // Existing grant was revoked and we used NO_FORCE, so we got info back | 137 // Existing grant was revoked and we used NO_FORCE, so we got info back |
| 113 // instead. | 138 // instead. |
| 114 if (interactive_) { | 139 if (should_prompt_for_scopes_) { |
| 115 install_ui_.reset(new ExtensionInstallPrompt(GetAssociatedWebContents())); | 140 install_ui_.reset(new ExtensionInstallPrompt(GetAssociatedWebContents())); |
| 116 ShowOAuthApprovalDialog(issue_advice); | 141 ShowOAuthApprovalDialog(issue_advice); |
| 117 } else { | 142 } else { |
| 118 error_ = identity_constants::kNoGrant; | 143 error_ = identity_constants::kNoGrant; |
| 119 SendResponse(false); | 144 SendResponse(false); |
| 120 Release(); // Balanced in RunImpl. | 145 Release(); // Balanced in RunImpl. |
| 121 } | 146 } |
| 122 } | 147 } |
| 123 | 148 |
| 124 void IdentityGetAuthTokenFunction::OnLoginUIClosed( | 149 void IdentityGetAuthTokenFunction::SigninSuccess(const std::string& token) { |
| 125 LoginUIService::LoginUI* ui) { | 150 refresh_token_ = token; |
| 126 StopObservingLoginService(); | 151 StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE); |
| 127 if (!StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE)) { | 152 } |
| 128 SendResponse(false); | 153 |
| 129 Release(); | 154 void IdentityGetAuthTokenFunction::SigninFailed() { |
| 130 } | 155 error_ = identity_constants::kUserNotSignedIn; |
| 156 SendResponse(false); |
| 157 Release(); |
| 131 } | 158 } |
| 132 | 159 |
| 133 void IdentityGetAuthTokenFunction::InstallUIProceed() { | 160 void IdentityGetAuthTokenFunction::InstallUIProceed() { |
| 134 DCHECK(install_ui_->record_oauth2_grant()); | 161 DCHECK(install_ui_->record_oauth2_grant()); |
| 135 // The user has accepted the scopes, so we may now force (recording a grant | 162 // The user has accepted the scopes, so we may now force (recording a grant |
| 136 // and receiving a token). | 163 // and receiving a token). |
| 137 bool success = StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE); | 164 StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE); |
| 138 DCHECK(success); | |
| 139 } | 165 } |
| 140 | 166 |
| 141 void IdentityGetAuthTokenFunction::InstallUIAbort(bool user_initiated) { | 167 void IdentityGetAuthTokenFunction::InstallUIAbort(bool user_initiated) { |
| 142 error_ = identity_constants::kUserRejected; | 168 error_ = identity_constants::kUserRejected; |
| 143 SendResponse(false); | 169 SendResponse(false); |
| 144 Release(); // Balanced in RunImpl. | 170 Release(); // Balanced in RunImpl. |
| 145 } | 171 } |
| 146 | 172 |
| 147 bool IdentityGetAuthTokenFunction::StartFlow(OAuth2MintTokenFlow::Mode mode) { | 173 void IdentityGetAuthTokenFunction::StartFlow(OAuth2MintTokenFlow::Mode mode) { |
| 148 if (!HasLoginToken()) { | 174 signin_flow_.reset(NULL); |
| 149 error_ = identity_constants::kUserNotSignedIn; | 175 mint_token_flow_.reset(CreateMintTokenFlow(mode)); |
| 150 return false; | 176 mint_token_flow_->Start(); |
| 151 } | |
| 152 | |
| 153 flow_.reset(CreateMintTokenFlow(mode)); | |
| 154 flow_->Start(); | |
| 155 return true; | |
| 156 } | |
| 157 | |
| 158 bool IdentityGetAuthTokenFunction::StartLogin() { | |
| 159 if (!interactive_) { | |
| 160 error_ = identity_constants::kUserNotSignedIn; | |
| 161 return false; | |
| 162 } | |
| 163 | |
| 164 ShowLoginPopup(); | |
| 165 return true; | |
| 166 } | |
| 167 | |
| 168 void IdentityGetAuthTokenFunction::StartObservingLoginService() { | |
| 169 LoginUIService* login_ui_service = | |
| 170 LoginUIServiceFactory::GetForProfile(profile()); | |
| 171 login_ui_service->AddObserver(this); | |
| 172 } | |
| 173 | |
| 174 void IdentityGetAuthTokenFunction::StopObservingLoginService() { | |
| 175 LoginUIService* login_ui_service = | |
| 176 LoginUIServiceFactory::GetForProfile(profile()); | |
| 177 login_ui_service->RemoveObserver(this); | |
| 178 } | 177 } |
| 179 | 178 |
| 180 void IdentityGetAuthTokenFunction::ShowLoginPopup() { | 179 void IdentityGetAuthTokenFunction::ShowLoginPopup() { |
| 181 StartObservingLoginService(); | 180 signin_flow_.reset(new IdentitySigninFlow(this, profile())); |
| 182 | 181 signin_flow_->Start(); |
| 183 LoginUIService* login_ui_service = | |
| 184 LoginUIServiceFactory::GetForProfile(profile()); | |
| 185 login_ui_service->ShowLoginPopup(); | |
| 186 } | 182 } |
| 187 | 183 |
| 188 void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( | 184 void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( |
| 189 const IssueAdviceInfo& issue_advice) { | 185 const IssueAdviceInfo& issue_advice) { |
| 190 install_ui_->ConfirmIssueAdvice(this, GetExtension(), issue_advice); | 186 install_ui_->ConfirmIssueAdvice(this, GetExtension(), issue_advice); |
| 191 } | 187 } |
| 192 | 188 |
| 193 OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow( | 189 OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow( |
| 194 OAuth2MintTokenFlow::Mode mode) { | 190 OAuth2MintTokenFlow::Mode mode) { |
| 195 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); | 191 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); |
| 196 TokenService* token_service = TokenServiceFactory::GetForProfile(profile()); | |
| 197 OAuth2MintTokenFlow* mint_token_flow = | 192 OAuth2MintTokenFlow* mint_token_flow = |
| 198 new OAuth2MintTokenFlow( | 193 new OAuth2MintTokenFlow( |
| 199 profile()->GetRequestContext(), | 194 profile()->GetRequestContext(), |
| 200 this, | 195 this, |
| 201 OAuth2MintTokenFlow::Parameters( | 196 OAuth2MintTokenFlow::Parameters( |
| 202 token_service->GetOAuth2LoginRefreshToken(), | 197 refresh_token_, |
| 203 GetExtension()->id(), | 198 GetExtension()->id(), |
| 204 oauth2_info.client_id, | 199 oauth2_info.client_id, |
| 205 oauth2_info.scopes, | 200 oauth2_info.scopes, |
| 206 mode)); | 201 mode)); |
| 207 #if defined(OS_CHROMEOS) | 202 #if defined(OS_CHROMEOS) |
| 208 if (chrome::IsRunningInForcedAppMode()) { | 203 if (chrome::IsRunningInForcedAppMode()) { |
| 209 std::string chrome_client_id; | 204 std::string chrome_client_id; |
| 210 std::string chrome_client_secret; | 205 std::string chrome_client_secret; |
| 211 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo( | 206 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo( |
| 212 &chrome_client_id, &chrome_client_secret)) { | 207 &chrome_client_id, &chrome_client_secret)) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 SendResponse(true); | 262 SendResponse(true); |
| 268 Release(); // Balanced in RunImpl. | 263 Release(); // Balanced in RunImpl. |
| 269 } | 264 } |
| 270 | 265 |
| 271 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure() { | 266 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure() { |
| 272 error_ = identity_constants::kInvalidRedirect; | 267 error_ = identity_constants::kInvalidRedirect; |
| 273 SendResponse(false); | 268 SendResponse(false); |
| 274 Release(); // Balanced in RunImpl. | 269 Release(); // Balanced in RunImpl. |
| 275 } | 270 } |
| 276 | 271 |
| 277 IdentityAPI::IdentityAPI(Profile* profile) { | 272 IdentityAPI::IdentityAPI(Profile* profile) |
| 273 : profile_(profile), |
| 274 signin_manager_(NULL), |
| 275 error_(GoogleServiceAuthError::NONE) { |
| 278 (new OAuth2ManifestHandler)->Register(); | 276 (new OAuth2ManifestHandler)->Register(); |
| 279 } | 277 } |
| 280 | 278 |
| 281 IdentityAPI::~IdentityAPI() { | 279 IdentityAPI::~IdentityAPI() { |
| 282 } | 280 } |
| 283 | 281 |
| 282 void IdentityAPI::Initialize() { |
| 283 signin_manager_ = SigninManagerFactory::GetForProfile(profile_); |
| 284 signin_manager_->signin_global_error()->AddProvider(this); |
| 285 |
| 286 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
| 287 registrar_.Add(this, |
| 288 chrome::NOTIFICATION_TOKEN_AVAILABLE, |
| 289 content::Source<TokenService>(token_service)); |
| 290 } |
| 291 |
| 292 void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) { |
| 293 if (!signin_manager_) |
| 294 Initialize(); |
| 295 |
| 296 error_ = error; |
| 297 signin_manager_->signin_global_error()->AuthStatusChanged(); |
| 298 } |
| 299 |
| 300 void IdentityAPI::Shutdown() { |
| 301 if (signin_manager_) |
| 302 signin_manager_->signin_global_error()->RemoveProvider(this); |
| 303 } |
| 304 |
| 284 static base::LazyInstance<ProfileKeyedAPIFactory<IdentityAPI> > | 305 static base::LazyInstance<ProfileKeyedAPIFactory<IdentityAPI> > |
| 285 g_factory = LAZY_INSTANCE_INITIALIZER; | 306 g_factory = LAZY_INSTANCE_INITIALIZER; |
| 286 | 307 |
| 287 // static | 308 // static |
| 288 ProfileKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { | 309 ProfileKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { |
| 289 return &g_factory.Get(); | 310 return &g_factory.Get(); |
| 290 } | 311 } |
| 291 | 312 |
| 313 GoogleServiceAuthError IdentityAPI::GetAuthStatus() const { |
| 314 return error_; |
| 315 } |
| 316 |
| 317 void IdentityAPI::Observe(int type, |
| 318 const content::NotificationSource& source, |
| 319 const content::NotificationDetails& details) { |
| 320 CHECK(type == chrome::NOTIFICATION_TOKEN_AVAILABLE); |
| 321 TokenService::TokenAvailableDetails* token_details = |
| 322 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); |
| 323 if (token_details->service() == |
| 324 GaiaConstants::kGaiaOAuth2LoginRefreshToken) { |
| 325 error_ = GoogleServiceAuthError::AuthErrorNone(); |
| 326 signin_manager_->signin_global_error()->AuthStatusChanged(); |
| 327 } |
| 328 } |
| 329 |
| 330 template <> |
| 331 void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() { |
| 332 DependsOn(ExtensionSystemFactory::GetInstance()); |
| 333 DependsOn(TokenServiceFactory::GetInstance()); |
| 334 DependsOn(SigninManagerFactory::GetInstance()); |
| 335 } |
| 336 |
| 292 } // namespace extensions | 337 } // namespace extensions |
| OLD | NEW |