OLD | NEW |
| (Empty) |
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/extensions/external_install_ui.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/lazy_instance.h" | |
11 #include "base/memory/ref_counted.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/message_loop/message_loop.h" | |
14 #include "base/metrics/histogram.h" | |
15 #include "base/scoped_observer.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "chrome/app/chrome_command_ids.h" | |
18 #include "chrome/browser/chrome_notification_types.h" | |
19 #include "chrome/browser/extensions/extension_install_prompt.h" | |
20 #include "chrome/browser/extensions/extension_install_ui.h" | |
21 #include "chrome/browser/extensions/extension_service.h" | |
22 #include "chrome/browser/extensions/extension_uninstall_dialog.h" | |
23 #include "chrome/browser/extensions/webstore_data_fetcher.h" | |
24 #include "chrome/browser/extensions/webstore_data_fetcher_delegate.h" | |
25 #include "chrome/browser/profiles/profile.h" | |
26 #include "chrome/browser/ui/browser.h" | |
27 #include "chrome/browser/ui/browser_finder.h" | |
28 #include "chrome/browser/ui/global_error/global_error.h" | |
29 #include "chrome/browser/ui/global_error/global_error_service.h" | |
30 #include "chrome/browser/ui/global_error/global_error_service_factory.h" | |
31 #include "chrome/common/extensions/manifest_url_handler.h" | |
32 #include "content/public/browser/notification_details.h" | |
33 #include "content/public/browser/notification_observer.h" | |
34 #include "content/public/browser/notification_registrar.h" | |
35 #include "content/public/browser/notification_source.h" | |
36 #include "extensions/browser/extension_registry.h" | |
37 #include "extensions/browser/extension_registry_observer.h" | |
38 #include "extensions/common/constants.h" | |
39 #include "grit/generated_resources.h" | |
40 #include "ui/base/l10n/l10n_util.h" | |
41 #include "ui/gfx/image/image.h" | |
42 #include "ui/gfx/image/image_skia_operations.h" | |
43 | |
44 namespace extensions { | |
45 | |
46 namespace { | |
47 | |
48 // Whether the external extension can use the streamlined bubble install flow. | |
49 bool UseBubbleInstall(const Extension* extension, bool is_new_profile) { | |
50 return ManifestURL::UpdatesFromGallery(extension) && !is_new_profile; | |
51 } | |
52 | |
53 } // namespace | |
54 | |
55 static const int kMenuCommandId = IDC_EXTERNAL_EXTENSION_ALERT; | |
56 | |
57 class ExternalInstallGlobalError; | |
58 | |
59 namespace extensions { | |
60 class ExtensionRegistry; | |
61 } | |
62 | |
63 // This class is refcounted to stay alive while we try and pull webstore data. | |
64 class ExternalInstallDialogDelegate | |
65 : public ExtensionInstallPrompt::Delegate, | |
66 public WebstoreDataFetcherDelegate, | |
67 public content::NotificationObserver, | |
68 public base::RefCountedThreadSafe<ExternalInstallDialogDelegate> { | |
69 public: | |
70 ExternalInstallDialogDelegate(Browser* browser, | |
71 ExtensionService* service, | |
72 const Extension* extension, | |
73 bool use_global_error); | |
74 | |
75 Browser* browser() { return browser_; } | |
76 | |
77 private: | |
78 friend class base::RefCountedThreadSafe<ExternalInstallDialogDelegate>; | |
79 friend class ExternalInstallGlobalError; | |
80 | |
81 virtual ~ExternalInstallDialogDelegate(); | |
82 | |
83 // ExtensionInstallPrompt::Delegate: | |
84 virtual void InstallUIProceed() OVERRIDE; | |
85 virtual void InstallUIAbort(bool user_initiated) OVERRIDE; | |
86 | |
87 // WebstoreDataFetcherDelegate: | |
88 virtual void OnWebstoreRequestFailure() OVERRIDE; | |
89 virtual void OnWebstoreResponseParseSuccess( | |
90 scoped_ptr<base::DictionaryValue> webstore_data) OVERRIDE; | |
91 virtual void OnWebstoreResponseParseFailure( | |
92 const std::string& error) OVERRIDE; | |
93 | |
94 // content::NotificationObserver: | |
95 virtual void Observe(int type, | |
96 const content::NotificationSource& source, | |
97 const content::NotificationDetails& details) OVERRIDE; | |
98 | |
99 // Show the install dialog to the user. | |
100 void ShowInstallUI(); | |
101 | |
102 // The UI for showing the install dialog when enabling. | |
103 scoped_ptr<ExtensionInstallPrompt> install_ui_; | |
104 scoped_ptr<ExtensionInstallPrompt::Prompt> prompt_; | |
105 | |
106 Browser* browser_; | |
107 base::WeakPtr<ExtensionService> service_weak_; | |
108 scoped_ptr<WebstoreDataFetcher> webstore_data_fetcher_; | |
109 content::NotificationRegistrar registrar_; | |
110 std::string extension_id_; | |
111 bool use_global_error_; | |
112 | |
113 DISALLOW_COPY_AND_ASSIGN(ExternalInstallDialogDelegate); | |
114 }; | |
115 | |
116 // Only shows a menu item, no bubble. Clicking the menu item shows | |
117 // an external install dialog. | |
118 class ExternalInstallMenuAlert : public GlobalErrorWithStandardBubble, | |
119 public content::NotificationObserver, | |
120 public ExtensionRegistryObserver { | |
121 public: | |
122 ExternalInstallMenuAlert(ExtensionService* service, | |
123 const Extension* extension); | |
124 virtual ~ExternalInstallMenuAlert(); | |
125 | |
126 // GlobalError implementation. | |
127 virtual Severity GetSeverity() OVERRIDE; | |
128 virtual bool HasMenuItem() OVERRIDE; | |
129 virtual int MenuItemCommandID() OVERRIDE; | |
130 virtual base::string16 MenuItemLabel() OVERRIDE; | |
131 virtual void ExecuteMenuItem(Browser* browser) OVERRIDE; | |
132 virtual bool HasBubbleView() OVERRIDE; | |
133 virtual base::string16 GetBubbleViewTitle() OVERRIDE; | |
134 virtual std::vector<base::string16> GetBubbleViewMessages() OVERRIDE; | |
135 virtual base::string16 GetBubbleViewAcceptButtonLabel() OVERRIDE; | |
136 virtual base::string16 GetBubbleViewCancelButtonLabel() OVERRIDE; | |
137 virtual void OnBubbleViewDidClose(Browser* browser) OVERRIDE; | |
138 virtual void BubbleViewAcceptButtonPressed(Browser* browser) OVERRIDE; | |
139 virtual void BubbleViewCancelButtonPressed(Browser* browser) OVERRIDE; | |
140 | |
141 protected: | |
142 ExtensionService* service_; | |
143 const Extension* extension_; | |
144 | |
145 private: | |
146 // Delete this instance after cleaning jobs. | |
147 void Clean(); | |
148 | |
149 // content::NotificationObserver implementation. | |
150 virtual void Observe(int type, | |
151 const content::NotificationSource& source, | |
152 const content::NotificationDetails& details) OVERRIDE; | |
153 | |
154 // ExtensionRegistryObserver implementation. | |
155 virtual void OnExtensionLoaded(content::BrowserContext* browser_context, | |
156 const Extension* extension) OVERRIDE; | |
157 | |
158 content::NotificationRegistrar registrar_; | |
159 | |
160 // Listen to extension load notifications. | |
161 ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> | |
162 extension_registry_observer_; | |
163 | |
164 DISALLOW_COPY_AND_ASSIGN(ExternalInstallMenuAlert); | |
165 }; | |
166 | |
167 // Shows a menu item and a global error bubble, replacing the install dialog. | |
168 class ExternalInstallGlobalError : public ExternalInstallMenuAlert { | |
169 public: | |
170 ExternalInstallGlobalError(ExtensionService* service, | |
171 const Extension* extension, | |
172 ExternalInstallDialogDelegate* delegate, | |
173 const ExtensionInstallPrompt::Prompt& prompt); | |
174 virtual ~ExternalInstallGlobalError(); | |
175 | |
176 virtual void ExecuteMenuItem(Browser* browser) OVERRIDE; | |
177 virtual bool HasBubbleView() OVERRIDE; | |
178 virtual gfx::Image GetBubbleViewIcon() OVERRIDE; | |
179 virtual base::string16 GetBubbleViewTitle() OVERRIDE; | |
180 virtual std::vector<base::string16> GetBubbleViewMessages() OVERRIDE; | |
181 virtual base::string16 GetBubbleViewAcceptButtonLabel() OVERRIDE; | |
182 virtual base::string16 GetBubbleViewCancelButtonLabel() OVERRIDE; | |
183 virtual void OnBubbleViewDidClose(Browser* browser) OVERRIDE; | |
184 virtual void BubbleViewAcceptButtonPressed(Browser* browser) OVERRIDE; | |
185 virtual void BubbleViewCancelButtonPressed(Browser* browser) OVERRIDE; | |
186 | |
187 protected: | |
188 // Ref-counted, but needs to be disposed of if we are dismissed without | |
189 // having been clicked (perhaps because the user enabled the extension | |
190 // manually). | |
191 ExternalInstallDialogDelegate* delegate_; | |
192 const ExtensionInstallPrompt::Prompt* prompt_; | |
193 | |
194 private: | |
195 DISALLOW_COPY_AND_ASSIGN(ExternalInstallGlobalError); | |
196 }; | |
197 | |
198 static void CreateExternalInstallGlobalError( | |
199 base::WeakPtr<ExtensionService> service, | |
200 const std::string& extension_id, | |
201 const ExtensionInstallPrompt::ShowParams& show_params, | |
202 ExtensionInstallPrompt::Delegate* prompt_delegate, | |
203 const ExtensionInstallPrompt::Prompt& prompt) { | |
204 if (!service.get()) | |
205 return; | |
206 const Extension* extension = service->GetInstalledExtension(extension_id); | |
207 if (!extension) | |
208 return; | |
209 GlobalErrorService* error_service = | |
210 GlobalErrorServiceFactory::GetForProfile(service->profile()); | |
211 if (error_service->GetGlobalErrorByMenuItemCommandID(kMenuCommandId)) | |
212 return; | |
213 | |
214 ExternalInstallDialogDelegate* delegate = | |
215 static_cast<ExternalInstallDialogDelegate*>(prompt_delegate); | |
216 ExternalInstallGlobalError* error_bubble = new ExternalInstallGlobalError( | |
217 service.get(), extension, delegate, prompt); | |
218 error_service->AddGlobalError(error_bubble); | |
219 // Show bubble immediately if possible. | |
220 if (delegate->browser()) | |
221 error_bubble->ShowBubbleView(delegate->browser()); | |
222 } | |
223 | |
224 static void ShowExternalInstallDialog( | |
225 ExtensionService* service, | |
226 Browser* browser, | |
227 const Extension* extension) { | |
228 // This object manages its own lifetime. | |
229 new ExternalInstallDialogDelegate(browser, service, extension, false); | |
230 } | |
231 | |
232 // ExternalInstallDialogDelegate -------------------------------------------- | |
233 | |
234 ExternalInstallDialogDelegate::ExternalInstallDialogDelegate( | |
235 Browser* browser, | |
236 ExtensionService* service, | |
237 const Extension* extension, | |
238 bool use_global_error) | |
239 : browser_(browser), | |
240 service_weak_(service->AsWeakPtr()), | |
241 extension_id_(extension->id()), | |
242 use_global_error_(use_global_error) { | |
243 AddRef(); // Balanced in Proceed or Abort. | |
244 | |
245 prompt_.reset(new ExtensionInstallPrompt::Prompt( | |
246 ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT)); | |
247 | |
248 // If we don't have a browser, we can't go to the webstore to fetch data. | |
249 // This should only happen in tests. | |
250 if (!browser) { | |
251 ShowInstallUI(); | |
252 return; | |
253 } | |
254 | |
255 // Make sure to be notified if the owning profile is destroyed. | |
256 registrar_.Add(this, | |
257 chrome::NOTIFICATION_PROFILE_DESTROYED, | |
258 content::Source<Profile>(browser->profile())); | |
259 | |
260 webstore_data_fetcher_.reset(new WebstoreDataFetcher( | |
261 this, | |
262 browser->profile()->GetRequestContext(), | |
263 GURL::EmptyGURL(), | |
264 extension->id())); | |
265 webstore_data_fetcher_->Start(); | |
266 } | |
267 | |
268 void ExternalInstallDialogDelegate::OnWebstoreRequestFailure() { | |
269 ShowInstallUI(); | |
270 } | |
271 | |
272 void ExternalInstallDialogDelegate::OnWebstoreResponseParseSuccess( | |
273 scoped_ptr<base::DictionaryValue> webstore_data) { | |
274 std::string localized_user_count; | |
275 double average_rating; | |
276 int rating_count; | |
277 if (!webstore_data->GetString(kUsersKey, &localized_user_count) || | |
278 !webstore_data->GetDouble(kAverageRatingKey, &average_rating) || | |
279 !webstore_data->GetInteger(kRatingCountKey, &rating_count)) { | |
280 // If we don't get a valid webstore response, short circuit, and continue | |
281 // to show a prompt without webstore data. | |
282 ShowInstallUI(); | |
283 return; | |
284 } | |
285 | |
286 bool show_user_count = true; | |
287 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count); | |
288 | |
289 prompt_->SetWebstoreData(localized_user_count, | |
290 show_user_count, | |
291 average_rating, | |
292 rating_count); | |
293 | |
294 ShowInstallUI(); | |
295 } | |
296 | |
297 void ExternalInstallDialogDelegate::OnWebstoreResponseParseFailure( | |
298 const std::string& error) { | |
299 ShowInstallUI(); | |
300 } | |
301 | |
302 void ExternalInstallDialogDelegate::Observe( | |
303 int type, | |
304 const content::NotificationSource& source, | |
305 const content::NotificationDetails& details) { | |
306 DCHECK_EQ(type, chrome::NOTIFICATION_PROFILE_DESTROYED); | |
307 // If the owning profile is destroyed, we need to abort so that we don't leak. | |
308 InstallUIAbort(false); // Not user initiated. | |
309 } | |
310 | |
311 void ExternalInstallDialogDelegate::ShowInstallUI() { | |
312 const Extension* extension = NULL; | |
313 if (!service_weak_.get() || | |
314 !(extension = service_weak_->GetInstalledExtension(extension_id_))) { | |
315 return; | |
316 } | |
317 install_ui_.reset( | |
318 ExtensionInstallUI::CreateInstallPromptWithBrowser(browser_)); | |
319 | |
320 const ExtensionInstallPrompt::ShowDialogCallback callback = | |
321 use_global_error_ ? | |
322 base::Bind(&CreateExternalInstallGlobalError, | |
323 service_weak_, | |
324 extension_id_) : | |
325 ExtensionInstallPrompt::GetDefaultShowDialogCallback(); | |
326 | |
327 install_ui_->ConfirmExternalInstall(this, extension, callback, *prompt_); | |
328 } | |
329 | |
330 ExternalInstallDialogDelegate::~ExternalInstallDialogDelegate() { | |
331 } | |
332 | |
333 void ExternalInstallDialogDelegate::InstallUIProceed() { | |
334 const Extension* extension = NULL; | |
335 if (service_weak_.get() && | |
336 (extension = service_weak_->GetInstalledExtension(extension_id_))) { | |
337 service_weak_->GrantPermissionsAndEnableExtension(extension); | |
338 } | |
339 Release(); | |
340 } | |
341 | |
342 void ExternalInstallDialogDelegate::InstallUIAbort(bool user_initiated) { | |
343 const Extension* extension = NULL; | |
344 | |
345 // Uninstall the extension if the abort was user initiated (and not, e.g., the | |
346 // result of the window closing). | |
347 // Otherwise, the extension will remain installed, but unacknowledged, so it | |
348 // will be prompted again. | |
349 if (user_initiated && | |
350 service_weak_.get() && | |
351 (extension = service_weak_->GetInstalledExtension(extension_id_))) { | |
352 service_weak_->UninstallExtension(extension_id_, false, NULL); | |
353 } | |
354 Release(); | |
355 } | |
356 | |
357 // ExternalInstallMenuAlert ------------------------------------------------- | |
358 | |
359 ExternalInstallMenuAlert::ExternalInstallMenuAlert(ExtensionService* service, | |
360 const Extension* extension) | |
361 : service_(service), | |
362 extension_(extension), | |
363 extension_registry_observer_(this) { | |
364 extension_registry_observer_.Add(ExtensionRegistry::Get(service->profile())); | |
365 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_REMOVED, | |
366 content::Source<Profile>(service->profile())); | |
367 } | |
368 | |
369 ExternalInstallMenuAlert::~ExternalInstallMenuAlert() { | |
370 } | |
371 | |
372 GlobalError::Severity ExternalInstallMenuAlert::GetSeverity() { | |
373 return SEVERITY_LOW; | |
374 } | |
375 | |
376 bool ExternalInstallMenuAlert::HasMenuItem() { | |
377 return true; | |
378 } | |
379 | |
380 int ExternalInstallMenuAlert::MenuItemCommandID() { | |
381 return kMenuCommandId; | |
382 } | |
383 | |
384 base::string16 ExternalInstallMenuAlert::MenuItemLabel() { | |
385 int id = -1; | |
386 if (extension_->is_app()) | |
387 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_APP; | |
388 else if (extension_->is_theme()) | |
389 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_THEME; | |
390 else | |
391 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_EXTENSION; | |
392 return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name())); | |
393 } | |
394 | |
395 void ExternalInstallMenuAlert::ExecuteMenuItem(Browser* browser) { | |
396 ShowExternalInstallDialog(service_, browser, extension_); | |
397 } | |
398 | |
399 bool ExternalInstallMenuAlert::HasBubbleView() { | |
400 return false; | |
401 } | |
402 base::string16 ExternalInstallMenuAlert::GetBubbleViewTitle() { | |
403 return base::string16(); | |
404 } | |
405 | |
406 std::vector<base::string16> ExternalInstallMenuAlert::GetBubbleViewMessages() { | |
407 return std::vector<base::string16>(); | |
408 } | |
409 | |
410 base::string16 ExternalInstallMenuAlert::GetBubbleViewAcceptButtonLabel() { | |
411 return base::string16(); | |
412 } | |
413 | |
414 base::string16 ExternalInstallMenuAlert::GetBubbleViewCancelButtonLabel() { | |
415 return base::string16(); | |
416 } | |
417 | |
418 void ExternalInstallMenuAlert::OnBubbleViewDidClose(Browser* browser) { | |
419 NOTREACHED(); | |
420 } | |
421 | |
422 void ExternalInstallMenuAlert::BubbleViewAcceptButtonPressed( | |
423 Browser* browser) { | |
424 NOTREACHED(); | |
425 } | |
426 | |
427 void ExternalInstallMenuAlert::BubbleViewCancelButtonPressed( | |
428 Browser* browser) { | |
429 NOTREACHED(); | |
430 } | |
431 | |
432 void ExternalInstallMenuAlert::OnExtensionLoaded( | |
433 content::BrowserContext* browser_context, | |
434 const Extension* extension) { | |
435 if (extension == extension_) | |
436 Clean(); | |
437 } | |
438 | |
439 void ExternalInstallMenuAlert::Observe( | |
440 int type, | |
441 const content::NotificationSource& source, | |
442 const content::NotificationDetails& details) { | |
443 // The error is invalidated if the extension has been loaded or removed. | |
444 DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_REMOVED); | |
445 const Extension* extension = content::Details<const Extension>(details).ptr(); | |
446 if (extension == extension_) | |
447 Clean(); | |
448 } | |
449 | |
450 void ExternalInstallMenuAlert::Clean() { | |
451 GlobalErrorService* error_service = | |
452 GlobalErrorServiceFactory::GetForProfile(service_->profile()); | |
453 error_service->RemoveGlobalError(this); | |
454 service_->AcknowledgeExternalExtension(extension_->id()); | |
455 delete this; | |
456 } | |
457 | |
458 // ExternalInstallGlobalError ----------------------------------------------- | |
459 | |
460 ExternalInstallGlobalError::ExternalInstallGlobalError( | |
461 ExtensionService* service, | |
462 const Extension* extension, | |
463 ExternalInstallDialogDelegate* delegate, | |
464 const ExtensionInstallPrompt::Prompt& prompt) | |
465 : ExternalInstallMenuAlert(service, extension), | |
466 delegate_(delegate), | |
467 prompt_(&prompt) { | |
468 } | |
469 | |
470 ExternalInstallGlobalError::~ExternalInstallGlobalError() { | |
471 if (delegate_) | |
472 delegate_->Release(); | |
473 } | |
474 | |
475 void ExternalInstallGlobalError::ExecuteMenuItem(Browser* browser) { | |
476 ShowBubbleView(browser); | |
477 } | |
478 | |
479 bool ExternalInstallGlobalError::HasBubbleView() { | |
480 return true; | |
481 } | |
482 | |
483 gfx::Image ExternalInstallGlobalError::GetBubbleViewIcon() { | |
484 if (prompt_->icon().IsEmpty()) | |
485 return GlobalErrorWithStandardBubble::GetBubbleViewIcon(); | |
486 // Scale icon to a reasonable size. | |
487 return gfx::Image(gfx::ImageSkiaOperations::CreateResizedImage( | |
488 *prompt_->icon().ToImageSkia(), | |
489 skia::ImageOperations::RESIZE_BEST, | |
490 gfx::Size(extension_misc::EXTENSION_ICON_SMALL, | |
491 extension_misc::EXTENSION_ICON_SMALL))); | |
492 } | |
493 | |
494 base::string16 ExternalInstallGlobalError::GetBubbleViewTitle() { | |
495 return prompt_->GetDialogTitle(); | |
496 } | |
497 | |
498 std::vector<base::string16> | |
499 ExternalInstallGlobalError::GetBubbleViewMessages() { | |
500 std::vector<base::string16> messages; | |
501 messages.push_back(prompt_->GetHeading()); | |
502 if (prompt_->GetPermissionCount()) { | |
503 messages.push_back(prompt_->GetPermissionsHeading()); | |
504 for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) { | |
505 messages.push_back(l10n_util::GetStringFUTF16( | |
506 IDS_EXTENSION_PERMISSION_LINE, | |
507 prompt_->GetPermission(i))); | |
508 } | |
509 } | |
510 // TODO(yoz): OAuth issue advice? | |
511 return messages; | |
512 } | |
513 | |
514 base::string16 ExternalInstallGlobalError::GetBubbleViewAcceptButtonLabel() { | |
515 return prompt_->GetAcceptButtonLabel(); | |
516 } | |
517 | |
518 base::string16 ExternalInstallGlobalError::GetBubbleViewCancelButtonLabel() { | |
519 return prompt_->GetAbortButtonLabel(); | |
520 } | |
521 | |
522 void ExternalInstallGlobalError::OnBubbleViewDidClose(Browser* browser) { | |
523 } | |
524 | |
525 void ExternalInstallGlobalError::BubbleViewAcceptButtonPressed( | |
526 Browser* browser) { | |
527 ExternalInstallDialogDelegate* delegate = delegate_; | |
528 delegate_ = NULL; | |
529 delegate->InstallUIProceed(); | |
530 } | |
531 | |
532 void ExternalInstallGlobalError::BubbleViewCancelButtonPressed( | |
533 Browser* browser) { | |
534 ExternalInstallDialogDelegate* delegate = delegate_; | |
535 delegate_ = NULL; | |
536 delegate->InstallUIAbort(true); | |
537 } | |
538 | |
539 // Public interface --------------------------------------------------------- | |
540 | |
541 void AddExternalInstallError(ExtensionService* service, | |
542 const Extension* extension, | |
543 bool is_new_profile) { | |
544 GlobalErrorService* error_service = | |
545 GlobalErrorServiceFactory::GetForProfile(service->profile()); | |
546 if (error_service->GetGlobalErrorByMenuItemCommandID(kMenuCommandId)) | |
547 return; | |
548 | |
549 if (UseBubbleInstall(extension, is_new_profile)) { | |
550 Browser* browser = NULL; | |
551 #if !defined(OS_ANDROID) | |
552 browser = chrome::FindTabbedBrowser(service->profile(), | |
553 true, | |
554 chrome::GetActiveDesktop()); | |
555 #endif | |
556 new ExternalInstallDialogDelegate(browser, service, extension, true); | |
557 } else { | |
558 error_service->AddGlobalError( | |
559 new ExternalInstallMenuAlert(service, extension)); | |
560 } | |
561 } | |
562 | |
563 void RemoveExternalInstallError(ExtensionService* service) { | |
564 GlobalErrorService* error_service = | |
565 GlobalErrorServiceFactory::GetForProfile(service->profile()); | |
566 GlobalError* error = error_service->GetGlobalErrorByMenuItemCommandID( | |
567 kMenuCommandId); | |
568 if (error) { | |
569 error_service->RemoveGlobalError(error); | |
570 delete error; | |
571 } | |
572 } | |
573 | |
574 bool HasExternalInstallError(ExtensionService* service) { | |
575 GlobalErrorService* error_service = | |
576 GlobalErrorServiceFactory::GetForProfile(service->profile()); | |
577 GlobalError* error = error_service->GetGlobalErrorByMenuItemCommandID( | |
578 kMenuCommandId); | |
579 return !!error; | |
580 } | |
581 | |
582 bool HasExternalInstallBubble(ExtensionService* service) { | |
583 GlobalErrorService* error_service = | |
584 GlobalErrorServiceFactory::GetForProfile(service->profile()); | |
585 GlobalError* error = error_service->GetGlobalErrorByMenuItemCommandID( | |
586 kMenuCommandId); | |
587 return error && error->HasBubbleView(); | |
588 } | |
589 | |
590 } // namespace extensions | |
OLD | NEW |