Home | History | Annotate | Download | only in extensions
      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