Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / extensions / api / messaging / incognito_connectability.cc
blobd647ae40bbae5cee39bbe005c642dc448e339e6d
1 // Copyright 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.
5 #include "chrome/browser/extensions/api/messaging/incognito_connectability.h"
7 #include "base/lazy_instance.h"
8 #include "base/logging.h"
9 #include "base/strings/string16.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/infobars/infobar_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "components/infobars/core/confirm_infobar_delegate.h"
15 #include "components/infobars/core/infobar.h"
16 #include "content/public/browser/web_contents.h"
17 #include "extensions/common/extension.h"
18 #include "ui/base/l10n/l10n_util.h"
20 using infobars::InfoBar;
21 using infobars::InfoBarManager;
23 namespace extensions {
25 namespace {
27 IncognitoConnectability::ScopedAlertTracker::Mode g_alert_mode =
28 IncognitoConnectability::ScopedAlertTracker::INTERACTIVE;
29 int g_alert_count = 0;
31 class IncognitoConnectabilityInfoBarDelegate : public ConfirmInfoBarDelegate {
32 public:
33 typedef base::Callback<void(
34 IncognitoConnectability::ScopedAlertTracker::Mode)> InfoBarCallback;
36 // Creates a confirmation infobar and delegate and adds the infobar to
37 // |infobar_service|.
38 static InfoBar* Create(InfoBarManager* infobar_manager,
39 const base::string16& message,
40 const InfoBarCallback& callback);
42 // Marks the infobar as answered so that the callback is not executed when the
43 // delegate is destroyed.
44 void set_answered() { answered_ = true; }
46 private:
47 IncognitoConnectabilityInfoBarDelegate(const base::string16& message,
48 const InfoBarCallback& callback);
49 ~IncognitoConnectabilityInfoBarDelegate() override;
51 // ConfirmInfoBarDelegate:
52 Type GetInfoBarType() const override;
53 base::string16 GetMessageText() const override;
54 base::string16 GetButtonLabel(InfoBarButton button) const override;
55 bool Accept() override;
56 bool Cancel() override;
58 base::string16 message_;
59 bool answered_;
60 InfoBarCallback callback_;
63 // static
64 InfoBar* IncognitoConnectabilityInfoBarDelegate::Create(
65 InfoBarManager* infobar_manager,
66 const base::string16& message,
67 const IncognitoConnectabilityInfoBarDelegate::InfoBarCallback& callback) {
68 return infobar_manager->AddInfoBar(infobar_manager->CreateConfirmInfoBar(
69 scoped_ptr<ConfirmInfoBarDelegate>(
70 new IncognitoConnectabilityInfoBarDelegate(message, callback))));
73 IncognitoConnectabilityInfoBarDelegate::IncognitoConnectabilityInfoBarDelegate(
74 const base::string16& message,
75 const InfoBarCallback& callback)
76 : message_(message), answered_(false), callback_(callback) {
79 IncognitoConnectabilityInfoBarDelegate::
80 ~IncognitoConnectabilityInfoBarDelegate() {
81 if (!answered_) {
82 // The infobar has closed without the user expressing an explicit
83 // preference. The current request should be denied but further requests
84 // should show an interactive prompt.
85 callback_.Run(IncognitoConnectability::ScopedAlertTracker::INTERACTIVE);
89 infobars::InfoBarDelegate::Type
90 IncognitoConnectabilityInfoBarDelegate::GetInfoBarType() const {
91 return PAGE_ACTION_TYPE;
94 base::string16 IncognitoConnectabilityInfoBarDelegate::GetMessageText() const {
95 return message_;
98 base::string16 IncognitoConnectabilityInfoBarDelegate::GetButtonLabel(
99 InfoBarButton button) const {
100 return l10n_util::GetStringUTF16(
101 (button == BUTTON_OK) ? IDS_PERMISSION_ALLOW : IDS_PERMISSION_DENY);
104 bool IncognitoConnectabilityInfoBarDelegate::Accept() {
105 callback_.Run(IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW);
106 answered_ = true;
107 return true;
110 bool IncognitoConnectabilityInfoBarDelegate::Cancel() {
111 callback_.Run(IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY);
112 answered_ = true;
113 return true;
116 } // namespace
118 IncognitoConnectability::ScopedAlertTracker::ScopedAlertTracker(Mode mode)
119 : last_checked_invocation_count_(g_alert_count) {
120 DCHECK_EQ(INTERACTIVE, g_alert_mode);
121 DCHECK_NE(INTERACTIVE, mode);
122 g_alert_mode = mode;
125 IncognitoConnectability::ScopedAlertTracker::~ScopedAlertTracker() {
126 DCHECK_NE(INTERACTIVE, g_alert_mode);
127 g_alert_mode = INTERACTIVE;
130 int IncognitoConnectability::ScopedAlertTracker::GetAndResetAlertCount() {
131 int result = g_alert_count - last_checked_invocation_count_;
132 last_checked_invocation_count_ = g_alert_count;
133 return result;
136 IncognitoConnectability::IncognitoConnectability(
137 content::BrowserContext* context)
138 : weak_factory_(this) {
139 CHECK(context->IsOffTheRecord());
142 IncognitoConnectability::~IncognitoConnectability() {
145 // static
146 IncognitoConnectability* IncognitoConnectability::Get(
147 content::BrowserContext* context) {
148 return BrowserContextKeyedAPIFactory<IncognitoConnectability>::Get(context);
151 void IncognitoConnectability::Query(
152 const Extension* extension,
153 content::WebContents* web_contents,
154 const GURL& url,
155 const base::Callback<void(bool)>& callback) {
156 GURL origin = url.GetOrigin();
157 if (origin.is_empty()) {
158 callback.Run(false);
159 return;
162 if (IsInMap(extension, origin, allowed_origins_)) {
163 callback.Run(true);
164 return;
167 if (IsInMap(extension, origin, disallowed_origins_)) {
168 callback.Run(false);
169 return;
172 PendingOrigin& pending_origin =
173 pending_origins_[make_pair(extension->id(), origin)];
174 InfoBarManager* infobar_manager =
175 InfoBarService::FromWebContents(web_contents);
176 TabContext& tab_context = pending_origin[infobar_manager];
177 tab_context.callbacks.push_back(callback);
178 if (tab_context.infobar) {
179 // This tab is already displaying an infobar for this extension and origin.
180 return;
183 // We need to ask the user.
184 ++g_alert_count;
186 switch (g_alert_mode) {
187 // Production code should always be using INTERACTIVE.
188 case ScopedAlertTracker::INTERACTIVE: {
189 int template_id =
190 extension->is_app()
191 ? IDS_EXTENSION_PROMPT_APP_CONNECT_FROM_INCOGNITO
192 : IDS_EXTENSION_PROMPT_EXTENSION_CONNECT_FROM_INCOGNITO;
193 tab_context.infobar = IncognitoConnectabilityInfoBarDelegate::Create(
194 infobar_manager, l10n_util::GetStringFUTF16(
195 template_id, base::UTF8ToUTF16(origin.spec()),
196 base::UTF8ToUTF16(extension->name())),
197 base::Bind(&IncognitoConnectability::OnInteractiveResponse,
198 weak_factory_.GetWeakPtr(), extension->id(), origin,
199 infobar_manager));
200 break;
203 // Testing code can override to always allow or deny.
204 case ScopedAlertTracker::ALWAYS_ALLOW:
205 case ScopedAlertTracker::ALWAYS_DENY:
206 OnInteractiveResponse(extension->id(), origin, infobar_manager,
207 g_alert_mode);
208 break;
212 IncognitoConnectability::TabContext::TabContext() : infobar(nullptr) {
215 IncognitoConnectability::TabContext::~TabContext() {
218 void IncognitoConnectability::OnInteractiveResponse(
219 const std::string& extension_id,
220 const GURL& origin,
221 InfoBarManager* infobar_manager,
222 ScopedAlertTracker::Mode response) {
223 switch (response) {
224 case ScopedAlertTracker::ALWAYS_ALLOW:
225 allowed_origins_[extension_id].insert(origin);
226 break;
227 case ScopedAlertTracker::ALWAYS_DENY:
228 disallowed_origins_[extension_id].insert(origin);
229 break;
230 default:
231 // Otherwise the user has not expressed an explicit preference and so
232 // nothing should be permanently recorded.
233 break;
236 DCHECK(ContainsKey(pending_origins_, make_pair(extension_id, origin)));
237 PendingOrigin& pending_origin =
238 pending_origins_[make_pair(extension_id, origin)];
239 DCHECK(ContainsKey(pending_origin, infobar_manager));
241 std::vector<base::Callback<void(bool)>> callbacks;
242 if (response == ScopedAlertTracker::INTERACTIVE) {
243 // No definitive answer for this extension and origin. Execute only the
244 // callbacks associated with this tab.
245 TabContext& tab_context = pending_origin[infobar_manager];
246 callbacks.swap(tab_context.callbacks);
247 pending_origin.erase(infobar_manager);
248 } else {
249 // We have a definitive answer for this extension and origin. Close all
250 // other infobars and answer all the callbacks.
251 for (const auto& map_entry : pending_origin) {
252 InfoBarManager* other_infobar_manager = map_entry.first;
253 const TabContext& other_tab_context = map_entry.second;
254 if (other_infobar_manager != infobar_manager) {
255 // Disarm the delegate so that it doesn't think the infobar has been
256 // dismissed.
257 IncognitoConnectabilityInfoBarDelegate* delegate =
258 static_cast<IncognitoConnectabilityInfoBarDelegate*>(
259 other_tab_context.infobar->delegate());
260 delegate->set_answered();
261 other_infobar_manager->RemoveInfoBar(other_tab_context.infobar);
263 callbacks.insert(callbacks.end(), other_tab_context.callbacks.begin(),
264 other_tab_context.callbacks.end());
266 pending_origins_.erase(make_pair(extension_id, origin));
269 DCHECK(!callbacks.empty());
270 for (const auto& callback : callbacks) {
271 callback.Run(response == ScopedAlertTracker::ALWAYS_ALLOW);
275 bool IncognitoConnectability::IsInMap(const Extension* extension,
276 const GURL& origin,
277 const ExtensionToOriginsMap& map) {
278 DCHECK_EQ(origin, origin.GetOrigin());
279 ExtensionToOriginsMap::const_iterator it = map.find(extension->id());
280 return it != map.end() && it->second.count(origin) > 0;
283 static base::LazyInstance<
284 BrowserContextKeyedAPIFactory<IncognitoConnectability> > g_factory =
285 LAZY_INSTANCE_INITIALIZER;
287 // static
288 BrowserContextKeyedAPIFactory<IncognitoConnectability>*
289 IncognitoConnectability::GetFactoryInstance() {
290 return g_factory.Pointer();
293 } // namespace extensions