blob: 68b6ae4070ba52649ba61dc1d830b2815f694016 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/protector/settings_change_global_error.h"
#include <bitset>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/protector/base_setting_change.h"
#include "chrome/browser/protector/settings_change_global_error_delegate.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
namespace protector {
namespace {
// Timeout before the global error is removed (wrench menu item disappears).
const int kMenuItemDisplayPeriodMs = 10*60*1000; // 10 min
// Unset bits indicate available command IDs.
static base::LazyInstance<
std::bitset<IDC_SHOW_SETTINGS_CHANGE_LAST -
IDC_SHOW_SETTINGS_CHANGE_FIRST + 1> > menu_ids =
LAZY_INSTANCE_INITIALIZER;
} // namespace
SettingsChangeGlobalError::SettingsChangeGlobalError(
BaseSettingChange* change,
SettingsChangeGlobalErrorDelegate* delegate)
: change_(change),
delegate_(delegate),
profile_(NULL),
closed_by_button_(false),
show_on_browser_activation_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
menu_id_(-1) {
DCHECK(delegate_);
for (int i = IDC_SHOW_SETTINGS_CHANGE_FIRST;
i <= IDC_SHOW_SETTINGS_CHANGE_LAST; i++) {
if (!menu_ids.Get()[i - IDC_SHOW_SETTINGS_CHANGE_FIRST]) {
menu_id_ = i;
menu_ids.Get().set(i - IDC_SHOW_SETTINGS_CHANGE_FIRST);
break;
}
}
DCHECK(menu_id_ >= 0) << "Out of command IDs for SettingsChangeGlobalError";
}
SettingsChangeGlobalError::~SettingsChangeGlobalError() {
if (profile_)
RemoveFromProfile();
if (menu_id_ >= 0)
menu_ids.Get().reset(menu_id_ - IDC_SHOW_SETTINGS_CHANGE_FIRST);
}
void SettingsChangeGlobalError::AddToProfile(Profile* profile,
bool show_bubble) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
profile_ = profile;
GlobalErrorServiceFactory::GetForProfile(profile_)->AddGlobalError(this);
BrowserList::AddObserver(this);
if (show_bubble) {
ShowBubble();
} else {
// Start inactivity timer.
BrowserThread::PostDelayedTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SettingsChangeGlobalError::OnInactiveTimeout,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kMenuItemDisplayPeriodMs));
}
}
void SettingsChangeGlobalError::RemoveFromProfile() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (profile_) {
GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError(this);
profile_ = NULL;
}
BrowserList::RemoveObserver(this);
// This will delete |this|.
delegate_->OnRemovedFromProfile(this);
}
void SettingsChangeGlobalError::ShowBubble() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(profile_);
Browser* browser = browser::FindTabbedBrowser(
profile_,
// match incognito
true);
if (browser)
ShowBubbleInBrowser(browser);
}
bool SettingsChangeGlobalError::HasBadge() {
return true;
}
int SettingsChangeGlobalError::GetBadgeResourceID() {
return change_->GetBadgeIconID();
}
bool SettingsChangeGlobalError::HasMenuItem() {
return true;
}
int SettingsChangeGlobalError::MenuItemCommandID() {
return menu_id_;
}
string16 SettingsChangeGlobalError::MenuItemLabel() {
return change_->GetBubbleTitle();
}
int SettingsChangeGlobalError::MenuItemIconResourceID() {
return change_->GetMenuItemIconID();
}
void SettingsChangeGlobalError::ExecuteMenuItem(Browser* browser) {
ShowBubbleInBrowser(browser);
}
bool SettingsChangeGlobalError::HasBubbleView() {
return true;
}
int SettingsChangeGlobalError::GetBubbleViewIconResourceID() {
return change_->GetBubbleIconID();
}
string16 SettingsChangeGlobalError::GetBubbleViewTitle() {
return change_->GetBubbleTitle();
}
string16 SettingsChangeGlobalError::GetBubbleViewMessage() {
return change_->GetBubbleMessage();
}
// The Accept and Revert buttons are swapped like the 'server' and 'client'
// concepts in X11. Accept button (the default one) discards changes
// (keeps using previous setting) while cancel button applies changes
// (switches to the new setting). This is sick and blows my mind. - ivankr
string16 SettingsChangeGlobalError::GetBubbleViewAcceptButtonLabel() {
return change_->GetDiscardButtonText();
}
string16 SettingsChangeGlobalError::GetBubbleViewCancelButtonLabel() {
return change_->GetApplyButtonText();
}
void SettingsChangeGlobalError::OnBubbleViewDidClose(Browser* browser) {
// The bubble may be closed as the result of RemoveFromProfile call when
// merging this error with another one.
if (!profile_)
return;
if (!closed_by_button_) {
BrowserThread::PostDelayedTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SettingsChangeGlobalError::OnInactiveTimeout,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kMenuItemDisplayPeriodMs));
#if !defined(TOOLKIT_GTK)
// TODO(ivankr): the logic for redisplaying bubble is disabled on Gtk, see
// http://crbug.com/115719.
if (browser->window() &&
!platform_util::IsWindowActive(browser->window()->GetNativeWindow())) {
// Bubble closed because the entire window lost activation, display
// again when a window gets active.
show_on_browser_activation_ = true;
}
#endif
} else {
RemoveFromProfile();
}
}
void SettingsChangeGlobalError::BubbleViewAcceptButtonPressed(
Browser* browser) {
closed_by_button_ = true;
delegate_->OnDiscardChange(this, browser);
}
void SettingsChangeGlobalError::BubbleViewCancelButtonPressed(
Browser* browser) {
closed_by_button_ = true;
delegate_->OnApplyChange(this, browser);
}
void SettingsChangeGlobalError::OnBrowserSetLastActive(
Browser* browser) {
if (show_on_browser_activation_ && browser && browser->is_type_tabbed()) {
// A tabbed browser window got activated, show the error bubble again.
// Calling ShowBubble() immediately from here does not always work because
// the old browser window may still have focus.
// Multiple posted ShowBubble() calls are fine since the first successful
// one will invalidate all the weak pointers.
// Note that ShowBubble() will display the bubble in the last active browser
// (which may not be |browser| at the moment ShowBubble() is executed).
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SettingsChangeGlobalError::ShowBubble,
weak_factory_.GetWeakPtr()));
}
}
void SettingsChangeGlobalError::ShowBubbleInBrowser(Browser* browser) {
show_on_browser_activation_ = false;
// Cancel any previously posted tasks so that the global error
// does not get removed on timeout while still showing the bubble.
weak_factory_.InvalidateWeakPtrs();
ShowBubbleView(browser);
}
void SettingsChangeGlobalError::OnInactiveTimeout() {
delegate_->OnDecisionTimeout(this);
RemoveFromProfile();
}
} // namespace protector