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
19using extensions::Extension;
20
21ExtensionEnableFlow::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
32ExtensionEnableFlow::~ExtensionEnableFlow() {
33}
34
35void ExtensionEnableFlow::StartForWebContents(
36    content::WebContents* parent_contents) {
37  parent_contents_ = parent_contents;
38  parent_window_ = NULL;
39  Run();
40}
41
42void ExtensionEnableFlow::StartForNativeWindow(
43    gfx::NativeWindow parent_window) {
44  parent_contents_ = NULL;
45  parent_window_ = parent_window;
46  Run();
47}
48
49void ExtensionEnableFlow::StartForCurrentlyNonexistentWindow(
50    base::Callback<gfx::NativeWindow(void)> window_getter) {
51  window_getter_ = window_getter;
52  Run();
53}
54
55void 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
83void 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
106void 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
114void 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
122void ExtensionEnableFlow::StopObserving() {
123  registrar_.RemoveAll();
124  extension_registry_observer_.RemoveAll();
125}
126
127void 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
135void ExtensionEnableFlow::OnExtensionLoaded(
136    content::BrowserContext* browser_context,
137    const Extension* extension) {
138  if (extension->id() == extension_id_) {
139    StopObserving();
140    CheckPermissionAndMaybePromptUser();
141  }
142}
143
144void 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
154void 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
170void ExtensionEnableFlow::InstallUIAbort(bool user_initiated) {
171  delegate_->ExtensionEnableFlowAborted(user_initiated);
172  // |delegate_| may delete us.
173}
174
175content::WebContents* ExtensionEnableFlow::OpenURL(
176    const content::OpenURLParams& params) {
177  chrome::ScopedTabbedBrowserDisplayer displayer(
178      profile_, chrome::GetActiveDesktop());
179  return displayer.browser()->OpenURL(params);
180}
181