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/extensions/extension_enable_flow.h" 6 7 #include "chrome/browser/chrome_notification_types.h" 8 #include "chrome/browser/extensions/extension_service.h" 9 #include "chrome/browser/profiles/profile.h" 10 #include "chrome/browser/ui/browser.h" 11 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h" 12 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" 13 #include "content/public/browser/notification_details.h" 14 #include "content/public/browser/notification_source.h" 15 #include "extensions/browser/extension_prefs.h" 16 #include "extensions/browser/extension_registry.h" 17 #include "extensions/browser/extension_system.h" 18 19 using extensions::Extension; 20 21 ExtensionEnableFlow::ExtensionEnableFlow(Profile* profile, 22 const std::string& extension_id, 23 ExtensionEnableFlowDelegate* delegate) 24 : profile_(profile), 25 extension_id_(extension_id), 26 delegate_(delegate), 27 parent_contents_(NULL), 28 parent_window_(NULL), 29 extension_registry_observer_(this) { 30 } 31 32 ExtensionEnableFlow::~ExtensionEnableFlow() { 33 } 34 35 void ExtensionEnableFlow::StartForWebContents( 36 content::WebContents* parent_contents) { 37 parent_contents_ = parent_contents; 38 parent_window_ = NULL; 39 Run(); 40 } 41 42 void ExtensionEnableFlow::StartForNativeWindow( 43 gfx::NativeWindow parent_window) { 44 parent_contents_ = NULL; 45 parent_window_ = parent_window; 46 Run(); 47 } 48 49 void ExtensionEnableFlow::StartForCurrentlyNonexistentWindow( 50 base::Callback<gfx::NativeWindow(void)> window_getter) { 51 window_getter_ = window_getter; 52 Run(); 53 } 54 55 void ExtensionEnableFlow::Run() { 56 ExtensionService* service = 57 extensions::ExtensionSystem::Get(profile_)->extension_service(); 58 const Extension* extension = service->GetExtensionById(extension_id_, true); 59 if (!extension) { 60 extension = extensions::ExtensionRegistry::Get(profile_)->GetExtensionById( 61 extension_id_, extensions::ExtensionRegistry::TERMINATED); 62 // It's possible (though unlikely) the app could have been uninstalled since 63 // the user clicked on it. 64 if (!extension) 65 return; 66 // If the app was terminated, reload it first. 67 service->ReloadExtension(extension_id_); 68 69 // ReloadExtension reallocates the Extension object. 70 extension = service->GetExtensionById(extension_id_, true); 71 72 // |extension| could be NULL for asynchronous load, such as the case of 73 // an unpacked extension. Wait for the load to continue the flow. 74 if (!extension) { 75 StartObserving(); 76 return; 77 } 78 } 79 80 CheckPermissionAndMaybePromptUser(); 81 } 82 83 void ExtensionEnableFlow::CheckPermissionAndMaybePromptUser() { 84 ExtensionService* service = 85 extensions::ExtensionSystem::Get(profile_)->extension_service(); 86 const Extension* extension = service->GetExtensionById(extension_id_, true); 87 if (!extension) { 88 delegate_->ExtensionEnableFlowAborted(false); // |delegate_| may delete us. 89 return; 90 } 91 92 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_); 93 if (!prefs->DidExtensionEscalatePermissions(extension_id_)) { 94 // Enable the extension immediately if its privileges weren't escalated. 95 // This is a no-op if the extension was previously terminated. 96 service->EnableExtension(extension_id_); 97 98 delegate_->ExtensionEnableFlowFinished(); // |delegate_| may delete us. 99 return; 100 } 101 102 CreatePrompt(); 103 prompt_->ConfirmReEnable(this, extension); 104 } 105 106 void ExtensionEnableFlow::CreatePrompt() { 107 if (!window_getter_.is_null()) 108 parent_window_ = window_getter_.Run(); 109 prompt_.reset(parent_contents_ ? 110 new ExtensionInstallPrompt(parent_contents_) : 111 new ExtensionInstallPrompt(profile_, parent_window_, this)); 112 } 113 114 void ExtensionEnableFlow::StartObserving() { 115 extension_registry_observer_.Add( 116 extensions::ExtensionRegistry::Get(profile_)); 117 registrar_.Add(this, 118 extensions::NOTIFICATION_EXTENSION_LOAD_ERROR, 119 content::Source<Profile>(profile_)); 120 } 121 122 void ExtensionEnableFlow::StopObserving() { 123 registrar_.RemoveAll(); 124 extension_registry_observer_.RemoveAll(); 125 } 126 127 void ExtensionEnableFlow::Observe(int type, 128 const content::NotificationSource& source, 129 const content::NotificationDetails& details) { 130 DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_LOAD_ERROR, type); 131 StopObserving(); 132 delegate_->ExtensionEnableFlowAborted(false); 133 } 134 135 void ExtensionEnableFlow::OnExtensionLoaded( 136 content::BrowserContext* browser_context, 137 const Extension* extension) { 138 if (extension->id() == extension_id_) { 139 StopObserving(); 140 CheckPermissionAndMaybePromptUser(); 141 } 142 } 143 144 void ExtensionEnableFlow::OnExtensionUninstalled( 145 content::BrowserContext* browser_context, 146 const Extension* extension, 147 extensions::UninstallReason reason) { 148 if (extension->id() == extension_id_) { 149 StopObserving(); 150 delegate_->ExtensionEnableFlowAborted(false); 151 } 152 } 153 154 void ExtensionEnableFlow::InstallUIProceed() { 155 ExtensionService* service = 156 extensions::ExtensionSystem::Get(profile_)->extension_service(); 157 158 // The extension can be uninstalled in another window while the UI was 159 // showing. Treat it as a cancellation and notify |delegate_|. 160 const Extension* extension = service->GetExtensionById(extension_id_, true); 161 if (!extension) { 162 delegate_->ExtensionEnableFlowAborted(true); 163 return; 164 } 165 166 service->GrantPermissionsAndEnableExtension(extension); 167 delegate_->ExtensionEnableFlowFinished(); // |delegate_| may delete us. 168 } 169 170 void ExtensionEnableFlow::InstallUIAbort(bool user_initiated) { 171 delegate_->ExtensionEnableFlowAborted(user_initiated); 172 // |delegate_| may delete us. 173 } 174 175 content::WebContents* ExtensionEnableFlow::OpenURL( 176 const content::OpenURLParams& params) { 177 chrome::ScopedTabbedBrowserDisplayer displayer( 178 profile_, chrome::GetActiveDesktop()); 179 return displayer.browser()->OpenURL(params); 180 } 181