1// Copyright (c) 2013 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/extensions/extension_message_bubble_controller.h"
6
7#include "base/bind.h"
8#include "base/metrics/histogram.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/extensions/extension_message_bubble.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/browser_finder.h"
14#include "chrome/common/url_constants.h"
15#include "content/public/browser/user_metrics.h"
16#include "extensions/browser/extension_registry.h"
17
18namespace extensions {
19
20////////////////////////////////////////////////////////////////////////////////
21// ExtensionMessageBubbleController::Delegate
22
23ExtensionMessageBubbleController::Delegate::Delegate() {
24}
25
26ExtensionMessageBubbleController::Delegate::~Delegate() {
27}
28
29void ExtensionMessageBubbleController::Delegate::RestrictToSingleExtension(
30    const std::string& extension_id) {
31  NOTIMPLEMENTED();  // Derived classes that need this should implement.
32}
33
34////////////////////////////////////////////////////////////////////////////////
35// ExtensionMessageBubbleController
36
37ExtensionMessageBubbleController::ExtensionMessageBubbleController(
38    Delegate* delegate, Profile* profile)
39    : profile_(profile),
40      user_action_(ACTION_BOUNDARY),
41      delegate_(delegate),
42      initialized_(false) {
43}
44
45ExtensionMessageBubbleController::~ExtensionMessageBubbleController() {
46}
47
48std::vector<base::string16>
49ExtensionMessageBubbleController::GetExtensionList() {
50  ExtensionIdList* list = GetOrCreateExtensionList();
51  if (list->empty())
52    return std::vector<base::string16>();
53
54  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
55  std::vector<base::string16> return_value;
56  for (ExtensionIdList::const_iterator it = list->begin();
57       it != list->end(); ++it) {
58    const Extension* extension =
59        registry->GetExtensionById(*it, ExtensionRegistry::EVERYTHING);
60    if (extension) {
61      return_value.push_back(base::UTF8ToUTF16(extension->name()));
62    } else {
63      return_value.push_back(
64          base::ASCIIToUTF16(std::string("(unknown name) ") + *it));
65      // TODO(finnur): Add this as a string to the grd, for next milestone.
66    }
67  }
68  return return_value;
69}
70
71const ExtensionIdList& ExtensionMessageBubbleController::GetExtensionIdList() {
72  return *GetOrCreateExtensionList();
73}
74
75bool ExtensionMessageBubbleController::CloseOnDeactivate() { return false; }
76
77void ExtensionMessageBubbleController::Show(ExtensionMessageBubble* bubble) {
78  // Wire up all the callbacks, to get notified what actions the user took.
79  base::Closure dismiss_button_callback =
80      base::Bind(&ExtensionMessageBubbleController::OnBubbleDismiss,
81      base::Unretained(this));
82  base::Closure action_button_callback =
83      base::Bind(&ExtensionMessageBubbleController::OnBubbleAction,
84      base::Unretained(this));
85  base::Closure link_callback =
86      base::Bind(&ExtensionMessageBubbleController::OnLinkClicked,
87      base::Unretained(this));
88  bubble->OnActionButtonClicked(action_button_callback);
89  bubble->OnDismissButtonClicked(dismiss_button_callback);
90  bubble->OnLinkClicked(link_callback);
91
92  bubble->Show();
93}
94
95void ExtensionMessageBubbleController::OnBubbleAction() {
96  DCHECK_EQ(ACTION_BOUNDARY, user_action_);
97  user_action_ = ACTION_EXECUTE;
98
99  delegate_->LogAction(ACTION_EXECUTE);
100  delegate_->PerformAction(*GetOrCreateExtensionList());
101  AcknowledgeExtensions();
102  delegate_->OnClose();
103}
104
105void ExtensionMessageBubbleController::OnBubbleDismiss() {
106  // OnBubbleDismiss() can be called twice when we receive multiple
107  // "OnWidgetDestroying" notifications (this can at least happen when we close
108  // a window with a notification open). Handle this gracefully.
109  if (user_action_ != ACTION_BOUNDARY) {
110    DCHECK(user_action_ == ACTION_DISMISS);
111    return;
112  }
113
114  user_action_ = ACTION_DISMISS;
115
116  delegate_->LogAction(ACTION_DISMISS);
117  AcknowledgeExtensions();
118  delegate_->OnClose();
119}
120
121void ExtensionMessageBubbleController::OnLinkClicked() {
122  DCHECK_EQ(ACTION_BOUNDARY, user_action_);
123  user_action_ = ACTION_LEARN_MORE;
124
125  delegate_->LogAction(ACTION_LEARN_MORE);
126  Browser* browser =
127      chrome::FindBrowserWithProfile(profile_, chrome::GetActiveDesktop());
128  if (browser) {
129    browser->OpenURL(
130        content::OpenURLParams(delegate_->GetLearnMoreUrl(),
131                               content::Referrer(),
132                               NEW_FOREGROUND_TAB,
133                               ui::PAGE_TRANSITION_LINK,
134                               false));
135  }
136  AcknowledgeExtensions();
137  delegate_->OnClose();
138}
139
140void ExtensionMessageBubbleController::AcknowledgeExtensions() {
141  ExtensionIdList* list = GetOrCreateExtensionList();
142  for (ExtensionIdList::const_iterator it = list->begin();
143       it != list->end(); ++it)
144    delegate_->AcknowledgeExtension(*it, user_action_);
145}
146
147ExtensionIdList* ExtensionMessageBubbleController::GetOrCreateExtensionList() {
148  if (!initialized_) {
149    scoped_ptr<const ExtensionSet> extension_set(
150        ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet());
151    for (ExtensionSet::const_iterator it = extension_set->begin();
152         it != extension_set->end(); ++it) {
153      std::string id = (*it)->id();
154      if (!delegate_->ShouldIncludeExtension(id))
155        continue;
156      extension_list_.push_back(id);
157    }
158
159    delegate_->LogExtensionCount(extension_list_.size());
160    initialized_ = true;
161  }
162
163  return &extension_list_;
164}
165
166}  // namespace extensions
167