Index: chrome/browser/ui/views/extensions/extension_message_bubble_view.cc |
diff --git a/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc b/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc |
index a2b70d479088a19c95ada0f5f36d95aad2d49982..578d693c599f3c1b9db7b2fdcbd6200f058d8c29 100644 |
--- a/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc |
+++ b/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc |
@@ -7,9 +7,24 @@ |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_util.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "chrome/browser/extensions/dev_mode_bubble_controller.h" |
+#include "chrome/browser/extensions/extension_action_manager.h" |
#include "chrome/browser/extensions/extension_message_bubble_controller.h" |
+#include "chrome/browser/extensions/extension_service.h" |
+#include "chrome/browser/extensions/extension_toolbar_model.h" |
+#include "chrome/browser/extensions/proxy_overridden_bubble_controller.h" |
+#include "chrome/browser/extensions/settings_api_bubble_controller.h" |
+#include "chrome/browser/extensions/settings_api_helpers.h" |
+#include "chrome/browser/extensions/suspicious_extension_bubble_controller.h" |
+#include "chrome/browser/profiles/profile.h" |
#include "chrome/browser/ui/view_ids.h" |
+#include "chrome/browser/ui/views/frame/browser_view.h" |
+#include "chrome/browser/ui/views/toolbar/browser_actions_container.h" |
+#include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h" |
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h" |
#include "chrome/grit/locale_settings.h" |
+#include "extensions/browser/extension_prefs.h" |
+#include "extensions/browser/extension_system.h" |
#include "ui/accessibility/ax_view_state.h" |
#include "ui/base/resource/resource_bundle.h" |
#include "ui/views/controls/button/label_button.h" |
@@ -20,6 +35,9 @@ |
#include "ui/views/widget/widget.h" |
namespace { |
+ |
+base::LazyInstance<std::set<Profile*> > g_profiles_evaluated = |
+ LAZY_INSTANCE_INITIALIZER; |
// Layout constants. |
const int kExtensionListPadding = 10; |
@@ -61,6 +79,21 @@ |
set_anchor_view_insets(gfx::Insets(5, 0, 5, 0)); |
} |
+void ExtensionMessageBubbleView::OnActionButtonClicked( |
+ const base::Closure& callback) { |
+ action_callback_ = callback; |
+} |
+ |
+void ExtensionMessageBubbleView::OnDismissButtonClicked( |
+ const base::Closure& callback) { |
+ dismiss_callback_ = callback; |
+} |
+ |
+void ExtensionMessageBubbleView::OnLinkClicked( |
+ const base::Closure& callback) { |
+ link_callback_ = callback; |
+} |
+ |
void ExtensionMessageBubbleView::Show() { |
// Not showing the bubble right away (during startup) has a few benefits: |
// We don't have to worry about focus being lost due to the Omnibox (or to |
@@ -81,7 +114,7 @@ |
// To catch Esc, we monitor destroy message. Unless the link has been clicked, |
// we assume Dismiss was the action taken. |
if (!link_clicked_ && !action_taken_) |
- controller_->OnBubbleDismiss(); |
+ dismiss_callback_.Run(); |
} |
//////////////////////////////////////////////////////////////////////////////// |
@@ -208,7 +241,7 @@ |
const ui::Event& event) { |
if (sender == action_button_) { |
action_taken_ = true; |
- controller_->OnBubbleAction(); |
+ action_callback_.Run(); |
} else { |
DCHECK_EQ(dismiss_button_, sender); |
} |
@@ -219,7 +252,7 @@ |
int event_flags) { |
DCHECK_EQ(learn_more_, source); |
link_clicked_ = true; |
- controller_->OnLinkClicked(); |
+ link_callback_.Run(); |
GetWidget()->Close(); |
} |
@@ -234,4 +267,243 @@ |
NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); |
} |
+//////////////////////////////////////////////////////////////////////////////// |
+// ExtensionMessageBubbleFactory |
+ |
+ExtensionMessageBubbleFactory::ExtensionMessageBubbleFactory( |
+ Profile* profile, |
+ ToolbarView* toolbar_view) |
+ : profile_(profile), |
+ toolbar_view_(toolbar_view), |
+ shown_suspicious_extensions_bubble_(false), |
+ shown_startup_override_extensions_bubble_(false), |
+ shown_proxy_override_extensions_bubble_(false), |
+ shown_dev_mode_extensions_bubble_(false), |
+ is_observing_(false), |
+ stage_(STAGE_START), |
+ container_(NULL), |
+ anchor_view_(NULL) {} |
+ |
+ExtensionMessageBubbleFactory::~ExtensionMessageBubbleFactory() { |
+ MaybeStopObserving(); |
+} |
+ |
+void ExtensionMessageBubbleFactory::MaybeShow(views::View* anchor_view) { |
+#if defined(OS_WIN) |
+ bool is_initial_check = IsInitialProfileCheck(profile_->GetOriginalProfile()); |
+ RecordProfileCheck(profile_->GetOriginalProfile()); |
+ |
+ // The list of suspicious extensions takes priority over the dev mode bubble |
+ // and the settings API bubble, since that needs to be shown as soon as we |
+ // disable something. The settings API bubble is shown on first startup after |
+ // an extension has changed the startup pages and it is acceptable if that |
+ // waits until the next startup because of the suspicious extension bubble. |
+ // The dev mode bubble is not time sensitive like the other two so we'll catch |
+ // the dev mode extensions on the next startup/next window that opens. That |
+ // way, we're not too spammy with the bubbles. |
+ if (!shown_suspicious_extensions_bubble_ && |
+ MaybeShowSuspiciousExtensionsBubble(anchor_view)) |
+ return; |
+ |
+ if (!shown_startup_override_extensions_bubble_ && |
+ is_initial_check && |
+ MaybeShowStartupOverrideExtensionsBubble(anchor_view)) |
+ return; |
+ |
+ if (!shown_proxy_override_extensions_bubble_ && |
+ MaybeShowProxyOverrideExtensionsBubble(anchor_view)) |
+ return; |
+ |
+ if (!shown_dev_mode_extensions_bubble_) |
+ MaybeShowDevModeExtensionsBubble(anchor_view); |
+#endif // OS_WIN |
+} |
+ |
+bool ExtensionMessageBubbleFactory::MaybeShowSuspiciousExtensionsBubble( |
+ views::View* anchor_view) { |
+ DCHECK(!shown_suspicious_extensions_bubble_); |
+ |
+ scoped_ptr<SuspiciousExtensionBubbleController> suspicious_extensions( |
+ new SuspiciousExtensionBubbleController(profile_)); |
+ if (!suspicious_extensions->ShouldShow()) |
+ return false; |
+ |
+ shown_suspicious_extensions_bubble_ = true; |
+ SuspiciousExtensionBubbleController* weak_controller = |
+ suspicious_extensions.get(); |
+ ExtensionMessageBubbleView* bubble_delegate = |
+ new ExtensionMessageBubbleView(anchor_view, |
+ views::BubbleBorder::TOP_RIGHT, |
+ suspicious_extensions.Pass()); |
+ |
+ views::BubbleDelegateView::CreateBubble(bubble_delegate); |
+ weak_controller->Show(bubble_delegate); |
+ |
+ return true; |
+} |
+ |
+bool ExtensionMessageBubbleFactory::MaybeShowStartupOverrideExtensionsBubble( |
+ views::View* anchor_view) { |
+#if !defined(OS_WIN) |
+ return false; |
+#else |
+ DCHECK(!shown_startup_override_extensions_bubble_); |
+ |
+ const Extension* extension = GetExtensionOverridingStartupPages(profile_); |
+ if (!extension) |
+ return false; |
+ |
+ scoped_ptr<SettingsApiBubbleController> settings_api_bubble( |
+ new SettingsApiBubbleController(profile_, |
+ BUBBLE_TYPE_STARTUP_PAGES)); |
+ if (!settings_api_bubble->ShouldShow(extension->id())) |
+ return false; |
+ |
+ shown_startup_override_extensions_bubble_ = true; |
+ PrepareToHighlightExtensions(settings_api_bubble.Pass(), anchor_view); |
+ return true; |
+#endif |
+} |
+ |
+bool ExtensionMessageBubbleFactory::MaybeShowProxyOverrideExtensionsBubble( |
+ views::View* anchor_view) { |
+#if !defined(OS_WIN) |
+ return false; |
+#else |
+ DCHECK(!shown_proxy_override_extensions_bubble_); |
+ |
+ const Extension* extension = GetExtensionOverridingProxy(profile_); |
+ if (!extension) |
+ return false; |
+ |
+ scoped_ptr<ProxyOverriddenBubbleController> proxy_bubble( |
+ new ProxyOverriddenBubbleController(profile_)); |
+ if (!proxy_bubble->ShouldShow(extension->id())) |
+ return false; |
+ |
+ shown_proxy_override_extensions_bubble_ = true; |
+ PrepareToHighlightExtensions(proxy_bubble.Pass(), anchor_view); |
+ return true; |
+#endif |
+} |
+ |
+bool ExtensionMessageBubbleFactory::MaybeShowDevModeExtensionsBubble( |
+ views::View* anchor_view) { |
+ DCHECK(!shown_dev_mode_extensions_bubble_); |
+ |
+ // Check the Developer Mode extensions. |
+ scoped_ptr<DevModeBubbleController> dev_mode_extensions( |
+ new DevModeBubbleController(profile_)); |
+ |
+ // Return early if we have none to show. |
+ if (!dev_mode_extensions->ShouldShow()) |
+ return false; |
+ |
+ shown_dev_mode_extensions_bubble_ = true; |
+ PrepareToHighlightExtensions(dev_mode_extensions.Pass(), anchor_view); |
+ return true; |
+} |
+ |
+void ExtensionMessageBubbleFactory::MaybeObserve() { |
+ if (!is_observing_) { |
+ is_observing_ = true; |
+ container_->AddObserver(this); |
+ } |
+} |
+ |
+void ExtensionMessageBubbleFactory::MaybeStopObserving() { |
+ if (is_observing_) { |
+ is_observing_ = false; |
+ container_->RemoveObserver(this); |
+ } |
+} |
+ |
+void ExtensionMessageBubbleFactory::RecordProfileCheck(Profile* profile) { |
+ g_profiles_evaluated.Get().insert(profile); |
+} |
+ |
+bool ExtensionMessageBubbleFactory::IsInitialProfileCheck(Profile* profile) { |
+ return g_profiles_evaluated.Get().count(profile) == 0; |
+} |
+ |
+void ExtensionMessageBubbleFactory::OnBrowserActionsContainerAnimationEnded() { |
+ MaybeStopObserving(); |
+ if (stage_ == STAGE_START) { |
+ HighlightExtensions(); |
+ } else if (stage_ == STAGE_HIGHLIGHTED) { |
+ ShowHighlightingBubble(); |
+ } else { // We shouldn't be observing if we've completed the process. |
+ NOTREACHED(); |
+ Finish(); |
+ } |
+} |
+ |
+void ExtensionMessageBubbleFactory::OnBrowserActionsContainerDestroyed() { |
+ // If the container associated with the bubble is destroyed, abandon the |
+ // process. |
+ Finish(); |
+} |
+ |
+void ExtensionMessageBubbleFactory::PrepareToHighlightExtensions( |
+ scoped_ptr<ExtensionMessageBubbleController> controller, |
+ views::View* anchor_view) { |
+ // We should be in the start stage (i.e., should not have a pending attempt to |
+ // show a bubble). |
+ DCHECK_EQ(stage_, STAGE_START); |
+ |
+ // Prepare to display and highlight the extensions before showing the bubble. |
+ // Since this is an asynchronous process, set member variables for later use. |
+ controller_ = controller.Pass(); |
+ anchor_view_ = anchor_view; |
+ container_ = toolbar_view_->browser_actions(); |
+ |
+ if (container_->animating()) |
+ MaybeObserve(); |
+ else |
+ HighlightExtensions(); |
+} |
+ |
+void ExtensionMessageBubbleFactory::HighlightExtensions() { |
+ DCHECK_EQ(STAGE_START, stage_); |
+ stage_ = STAGE_HIGHLIGHTED; |
+ |
+ const ExtensionIdList extension_list = controller_->GetExtensionIdList(); |
+ DCHECK(!extension_list.empty()); |
+ ExtensionToolbarModel::Get(profile_)->HighlightExtensions(extension_list); |
+ if (container_->animating()) |
+ MaybeObserve(); |
+ else |
+ ShowHighlightingBubble(); |
+} |
+ |
+void ExtensionMessageBubbleFactory::ShowHighlightingBubble() { |
+ DCHECK_EQ(stage_, STAGE_HIGHLIGHTED); |
+ stage_ = STAGE_COMPLETE; |
+ |
+ views::View* reference_view = NULL; |
+ if (container_->num_toolbar_actions() > 0u) |
+ reference_view = container_->GetToolbarActionViewAt(0); |
+ if (reference_view && reference_view->visible()) |
+ anchor_view_ = reference_view; |
+ |
+ ExtensionMessageBubbleController* weak_controller = controller_.get(); |
+ ExtensionMessageBubbleView* bubble_delegate = |
+ new ExtensionMessageBubbleView( |
+ anchor_view_, |
+ views::BubbleBorder::TOP_RIGHT, |
+ scoped_ptr<ExtensionMessageBubbleController>( |
+ controller_.release())); |
+ views::BubbleDelegateView::CreateBubble(bubble_delegate); |
+ weak_controller->Show(bubble_delegate); |
+ |
+ Finish(); |
+} |
+ |
+void ExtensionMessageBubbleFactory::Finish() { |
+ MaybeStopObserving(); |
+ controller_.reset(); |
+ anchor_view_ = NULL; |
+ container_ = NULL; |
+} |
+ |
} // namespace extensions |