blob: 30df1b691786bb9aa128219657bc555356cf5a32 [file] [log] [blame]
// Copyright 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/ui/browser_instant_controller.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/omnibox/location_bar.h"
#include "chrome/browser/ui/omnibox/omnibox_view.h"
#include "chrome/browser/ui/search/search.h"
#include "chrome/browser/ui/search/search_tab_helper.h"
#include "chrome/browser/ui/tab_contents/tab_contents.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"
#include "grit/theme_resources.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/sys_color_change_listener.h"
namespace {
const char* GetInstantPrefName(Profile* profile) {
return chrome::search::IsInstantExtendedAPIEnabled(profile) ?
prefs::kInstantExtendedEnabled : prefs::kInstantEnabled;
}
}
namespace chrome {
////////////////////////////////////////////////////////////////////////////////
// BrowserInstantController, public:
BrowserInstantController::BrowserInstantController(Browser* browser)
: browser_(browser),
instant_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
chrome::search::IsInstantExtendedAPIEnabled(browser->profile())),
instant_unload_handler_(browser),
initialized_theme_info_(false),
theme_area_height_(0) {
profile_pref_registrar_.Init(browser_->profile()->GetPrefs());
profile_pref_registrar_.Add(
GetInstantPrefName(browser_->profile()),
base::Bind(&BrowserInstantController::ResetInstant,
base::Unretained(this)));
ResetInstant();
browser_->search_model()->AddObserver(this);
#if defined(ENABLE_THEMES)
// Listen for theme installation.
registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
content::Source<ThemeService>(
ThemeServiceFactory::GetForProfile(browser_->profile())));
#endif // defined(ENABLE_THEMES)
}
BrowserInstantController::~BrowserInstantController() {
browser_->search_model()->RemoveObserver(this);
}
bool BrowserInstantController::IsInstantEnabled(Profile* profile) {
return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
profile->GetPrefs()->GetBoolean(GetInstantPrefName(profile));
}
void BrowserInstantController::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false,
PrefService::SYNCABLE_PREF);
prefs->RegisterBooleanPref(prefs::kInstantExtendedEnabled, true,
PrefService::SYNCABLE_PREF);
prefs->RegisterBooleanPref(prefs::kInstantEnabled, false,
PrefService::SYNCABLE_PREF);
}
bool BrowserInstantController::OpenInstant(WindowOpenDisposition disposition) {
// Unsupported dispositions.
if (disposition == NEW_BACKGROUND_TAB || disposition == NEW_WINDOW)
return false;
// The omnibox currently doesn't use other dispositions, so we don't attempt
// to handle them. If you hit this DCHECK file a bug and I'll (sky) add
// support for the new disposition.
DCHECK(disposition == CURRENT_TAB ||
disposition == NEW_FOREGROUND_TAB) << disposition;
return instant_.CommitIfPossible(disposition == CURRENT_TAB ?
INSTANT_COMMIT_PRESSED_ENTER : INSTANT_COMMIT_PRESSED_ALT_ENTER);
}
void BrowserInstantController::CommitInstant(content::WebContents* preview,
bool in_new_tab) {
if (in_new_tab) {
// TabStripModel takes ownership of |preview|.
browser_->tab_strip_model()->AddWebContents(preview, -1,
instant_.last_transition_type(), TabStripModel::ADD_ACTIVE);
} else {
int index = browser_->tab_strip_model()->active_index();
DCHECK_NE(TabStripModel::kNoTab, index);
content::WebContents* active_tab =
browser_->tab_strip_model()->GetWebContentsAt(index);
// TabStripModel takes ownership of |preview|.
browser_->tab_strip_model()->ReplaceWebContentsAt(index, preview);
// InstantUnloadHandler takes ownership of |active_tab|.
instant_unload_handler_.RunUnloadListenersOrDestroy(active_tab, index);
GURL url = preview->GetURL();
DCHECK(browser_->profile()->GetExtensionService());
if (browser_->profile()->GetExtensionService()->IsInstalledApp(url)) {
AppLauncherHandler::RecordAppLaunchType(
extension_misc::APP_LAUNCH_OMNIBOX_INSTANT);
}
}
}
void BrowserInstantController::SetInstantSuggestion(
const InstantSuggestion& suggestion) {
browser_->window()->GetLocationBar()->SetInstantSuggestion(suggestion);
}
gfx::Rect BrowserInstantController::GetInstantBounds() {
return browser_->window()->GetInstantBounds();
}
void BrowserInstantController::InstantPreviewFocused() {
// NOTE: This is only invoked on aura.
browser_->window()->WebContentsFocused(instant_.GetPreviewContents());
}
void BrowserInstantController::FocusOmniboxInvisibly() {
OmniboxView* omnibox_view = browser_->window()->GetLocationBar()->
GetLocationEntry();
omnibox_view->SetFocus();
omnibox_view->model()->SetCaretVisibility(false);
}
content::WebContents* BrowserInstantController::GetActiveWebContents() const {
return browser_->tab_strip_model()->GetActiveWebContents();
}
void BrowserInstantController::ActiveTabChanged() {
instant_.ActiveTabChanged();
}
void BrowserInstantController::TabDeactivated(content::WebContents* contents) {
instant_.TabDeactivated(contents);
}
void BrowserInstantController::SetContentHeight(int height) {
OnThemeAreaHeightChanged(height);
}
void BrowserInstantController::UpdateThemeInfoForPreview() {
// Update theme background info and theme area height.
// Initialize |theme_info| if necessary.
// |OnThemeChanged| also updates theme area height if necessary.
if (!initialized_theme_info_)
OnThemeChanged(ThemeServiceFactory::GetForProfile(browser_->profile()));
else
OnThemeChanged(NULL);
}
void BrowserInstantController::OpenURLInCurrentTab(
const GURL& url,
content::PageTransition transition) {
browser_->OpenURL(content::OpenURLParams(url,
content::Referrer(),
CURRENT_TAB,
transition,
false));
}
void BrowserInstantController::SetMarginSize(int start, int end) {
instant_.SetMarginSize(start, end);
}
void BrowserInstantController::ResetInstant() {
instant_.SetInstantEnabled(IsInstantEnabled(browser_->profile()));
}
////////////////////////////////////////////////////////////////////////////////
// BrowserInstantController, search::SearchModelObserver implementation:
void BrowserInstantController::ModeChanged(const search::Mode& old_mode,
const search::Mode& new_mode) {
// If mode is now |NTP|, send theme-related information to instant.
if (new_mode.is_ntp())
UpdateThemeInfoForPreview();
instant_.SearchModeChanged(old_mode, new_mode);
}
////////////////////////////////////////////////////////////////////////////////
// BrowserInstantController, content::NotificationObserver implementation:
void BrowserInstantController::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
#if defined(ENABLE_THEMES)
DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
OnThemeChanged(content::Source<ThemeService>(source).ptr());
#endif // defined(ENABLE_THEMES)
}
void BrowserInstantController::OnThemeChanged(ThemeService* theme_service) {
if (theme_service) { // Get theme information from theme service.
theme_info_ = ThemeBackgroundInfo();
// Set theme background color.
SkColor background_color =
theme_service->GetColor(ThemeService::COLOR_NTP_BACKGROUND);
if (gfx::IsInvertedColorScheme())
background_color = color_utils::InvertColor(background_color);
theme_info_.color_r = SkColorGetR(background_color);
theme_info_.color_g = SkColorGetG(background_color);
theme_info_.color_b = SkColorGetB(background_color);
theme_info_.color_a = SkColorGetA(background_color);
if (theme_service->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
// Set theme id for theme background image url.
theme_info_.theme_id = theme_service->GetThemeID();
// Set theme background image horizontal alignment.
int alignment = 0;
theme_service->GetDisplayProperty(ThemeService::NTP_BACKGROUND_ALIGNMENT,
&alignment);
if (alignment & ThemeService::ALIGN_LEFT) {
theme_info_.image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_LEFT;
} else if (alignment & ThemeService::ALIGN_RIGHT) {
theme_info_.image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_RIGHT;
} else { // ALIGN_CENTER
theme_info_.image_horizontal_alignment =
THEME_BKGRND_IMAGE_ALIGN_CENTER;
}
// Set theme background image vertical alignment.
if (alignment & ThemeService::ALIGN_TOP)
theme_info_.image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_TOP;
else if (alignment & ThemeService::ALIGN_BOTTOM)
theme_info_.image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_BOTTOM;
else // ALIGN_CENTER
theme_info_.image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
// Set theme background image tiling.
int tiling = 0;
theme_service->GetDisplayProperty(ThemeService::NTP_BACKGROUND_TILING,
&tiling);
switch (tiling) {
case ThemeService::NO_REPEAT:
theme_info_.image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT;
break;
case ThemeService::REPEAT_X:
theme_info_.image_tiling = THEME_BKGRND_IMAGE_REPEAT_X;
break;
case ThemeService::REPEAT_Y:
theme_info_.image_tiling = THEME_BKGRND_IMAGE_REPEAT_Y;
break;
case ThemeService::REPEAT:
theme_info_.image_tiling = THEME_BKGRND_IMAGE_REPEAT;
break;
}
// Set theme background image height.
gfx::ImageSkia* image = theme_service->GetImageSkiaNamed(
IDR_THEME_NTP_BACKGROUND);
DCHECK(image);
theme_info_.image_height = image->height();
}
initialized_theme_info_ = true;
}
DCHECK(initialized_theme_info_);
if (browser_->search_model()->mode().is_ntp()) {
instant_.ThemeChanged(theme_info_);
// Theme area height is only sent to preview for non-top-aligned images;
// new theme may have a different alignment that requires preview to know
// theme area height.
OnThemeAreaHeightChanged(theme_area_height_);
}
}
void BrowserInstantController::OnThemeAreaHeightChanged(int height) {
theme_area_height_ = height;
// Notify preview only if mode is |NTP| and theme background image is not top-
// aligned; top-aligned images don't need theme area height to determine which
// part of the image overlay should draw, 'cos the origin is top-left.
if (!browser_->search_model()->mode().is_ntp() ||
theme_info_.theme_id.empty() ||
theme_info_.image_vertical_alignment == THEME_BKGRND_IMAGE_ALIGN_TOP) {
return;
}
instant_.ThemeAreaHeightChanged(theme_area_height_);
}
} // namespace chrome