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