12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/extensions/extension_enable_flow.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
120f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
13ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "content/public/browser/notification_details.h"
14ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "content/public/browser/notification_source.h"
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "extensions/browser/extension_prefs.h"
16effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "extensions/browser/extension_registry.h"
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Extension;
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ExtensionEnableFlow::ExtensionEnableFlow(Profile* profile,
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         const std::string& extension_id,
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         ExtensionEnableFlowDelegate* delegate)
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : profile_(profile),
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extension_id_(extension_id),
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      delegate_(delegate),
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      parent_contents_(NULL),
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      parent_window_(NULL),
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      extension_registry_observer_(this) {
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ExtensionEnableFlow::~ExtensionEnableFlow() {
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionEnableFlow::StartForWebContents(
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* parent_contents) {
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parent_contents_ = parent_contents;
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parent_window_ = NULL;
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Run();
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionEnableFlow::StartForNativeWindow(
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::NativeWindow parent_window) {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parent_contents_ = NULL;
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parent_window_ = parent_window;
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Run();
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void ExtensionEnableFlow::StartForCurrentlyNonexistentWindow(
5068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    base::Callback<gfx::NativeWindow(void)> window_getter) {
5168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  window_getter_ = window_getter;
5268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  Run();
5368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
5468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionEnableFlow::Run() {
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionService* service =
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile_)->extension_service();
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension = service->GetExtensionById(extension_id_, true);
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension) {
60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    extension = extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        extension_id_, extensions::ExtensionRegistry::TERMINATED);
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // It's possible (though unlikely) the app could have been uninstalled since
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // the user clicked on it.
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!extension)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
66ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // If the app was terminated, reload it first.
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    service->ReloadExtension(extension_id_);
68ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
69ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // ReloadExtension reallocates the Extension object.
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    extension = service->GetExtensionById(extension_id_, true);
71ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
72ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // |extension| could be NULL for asynchronous load, such as the case of
73ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // an unpacked extension. Wait for the load to continue the flow.
74ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (!extension) {
75ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      StartObserving();
76ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      return;
77ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    }
78ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
79ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
80ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  CheckPermissionAndMaybePromptUser();
81ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
82ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
83ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ExtensionEnableFlow::CheckPermissionAndMaybePromptUser() {
84ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ExtensionService* service =
85ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      extensions::ExtensionSystem::Get(profile_)->extension_service();
86ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  const Extension* extension = service->GetExtensionById(extension_id_, true);
87ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!extension) {
88ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    delegate_->ExtensionEnableFlowAborted(false);  // |delegate_| may delete us.
89ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!prefs->DidExtensionEscalatePermissions(extension_id_)) {
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Enable the extension immediately if its privileges weren't escalated.
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // This is a no-op if the extension was previously terminated.
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    service->EnableExtension(extension_id_);
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    delegate_->ExtensionEnableFlowFinished();  // |delegate_| may delete us.
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CreatePrompt();
1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  prompt_->ConfirmReEnable(this, extension);
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionEnableFlow::CreatePrompt() {
10768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (!window_getter_.is_null())
10868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    parent_window_ = window_getter_.Run();
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  prompt_.reset(parent_contents_ ?
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new ExtensionInstallPrompt(parent_contents_) :
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new ExtensionInstallPrompt(profile_, parent_window_, this));
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
114ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ExtensionEnableFlow::StartObserving() {
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  extension_registry_observer_.Add(
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      extensions::ExtensionRegistry::Get(profile_));
1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  registrar_.Add(this,
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 extensions::NOTIFICATION_EXTENSION_LOAD_ERROR,
119ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                 content::Source<Profile>(profile_));
120ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
121ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
122ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ExtensionEnableFlow::StopObserving() {
123ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  registrar_.RemoveAll();
12446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  extension_registry_observer_.RemoveAll();
125ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
126ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
127ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ExtensionEnableFlow::Observe(int type,
128ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                  const content::NotificationSource& source,
129ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                  const content::NotificationDetails& details) {
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_LOAD_ERROR, type);
13146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  StopObserving();
13246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  delegate_->ExtensionEnableFlowAborted(false);
133ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
134ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ExtensionEnableFlow::OnExtensionLoaded(
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::BrowserContext* browser_context,
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const Extension* extension) {
138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (extension->id() == extension_id_) {
139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    StopObserving();
140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    CheckPermissionAndMaybePromptUser();
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
14446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void ExtensionEnableFlow::OnExtensionUninstalled(
14546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    content::BrowserContext* browser_context,
1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const Extension* extension,
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    extensions::UninstallReason reason) {
14846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (extension->id() == extension_id_) {
14946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    StopObserving();
15046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    delegate_->ExtensionEnableFlowAborted(false);
15146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
15246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
15346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionEnableFlow::InstallUIProceed() {
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionService* service =
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile_)->extension_service();
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The extension can be uninstalled in another window while the UI was
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // showing. Treat it as a cancellation and notify |delegate_|.
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension = service->GetExtensionById(extension_id_, true);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension) {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    delegate_->ExtensionEnableFlowAborted(true);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  service->GrantPermissionsAndEnableExtension(extension);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delegate_->ExtensionEnableFlowFinished();  // |delegate_| may delete us.
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionEnableFlow::InstallUIAbort(bool user_initiated) {
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delegate_->ExtensionEnableFlowAborted(user_initiated);
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |delegate_| may delete us.
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)content::WebContents* ExtensionEnableFlow::OpenURL(
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const content::OpenURLParams& params) {
1770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  chrome::ScopedTabbedBrowserDisplayer displayer(
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      profile_, chrome::GetActiveDesktop());
1790f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  return displayer.browser()->OpenURL(params);
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
181