1 // Copyright (c) 2011 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/ui/webui/options/browser_options_handler.h" 6 7 #include "base/basictypes.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/memory/singleton.h" 10 #include "base/string_number_conversions.h" 11 #include "base/utf_string_conversions.h" 12 #include "base/values.h" 13 #include "chrome/browser/autocomplete/autocomplete.h" 14 #include "chrome/browser/autocomplete/autocomplete_match.h" 15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/custom_home_pages_table_model.h" 17 #include "chrome/browser/instant/instant_confirm_dialog.h" 18 #include "chrome/browser/metrics/user_metrics.h" 19 #include "chrome/browser/net/url_fixer_upper.h" 20 #include "chrome/browser/prefs/pref_service.h" 21 #include "chrome/browser/prefs/session_startup_pref.h" 22 #include "chrome/browser/profiles/profile.h" 23 #include "chrome/browser/search_engines/template_url.h" 24 #include "chrome/browser/search_engines/template_url_model.h" 25 #include "chrome/browser/ui/options/options_window.h" 26 #include "chrome/browser/ui/webui/favicon_source.h" 27 #include "chrome/browser/ui/webui/options/options_managed_banner_handler.h" 28 #include "chrome/common/pref_names.h" 29 #include "chrome/installer/util/browser_distribution.h" 30 #include "content/browser/browser_thread.h" 31 #include "content/common/notification_service.h" 32 #include "content/common/notification_source.h" 33 #include "content/common/notification_type.h" 34 #include "grit/chromium_strings.h" 35 #include "grit/generated_resources.h" 36 #include "ui/base/l10n/l10n_util.h" 37 38 BrowserOptionsHandler::BrowserOptionsHandler() 39 : template_url_model_(NULL), startup_custom_pages_table_model_(NULL) { 40 #if !defined(OS_MACOSX) 41 default_browser_worker_ = new ShellIntegration::DefaultBrowserWorker(this); 42 #endif 43 } 44 45 BrowserOptionsHandler::~BrowserOptionsHandler() { 46 if (default_browser_worker_.get()) 47 default_browser_worker_->ObserverDestroyed(); 48 if (template_url_model_) 49 template_url_model_->RemoveObserver(this); 50 } 51 52 void BrowserOptionsHandler::GetLocalizedValues( 53 DictionaryValue* localized_strings) { 54 DCHECK(localized_strings); 55 56 static OptionsStringResource resources[] = { 57 { "startupGroupName", IDS_OPTIONS_STARTUP_GROUP_NAME }, 58 { "startupShowDefaultAndNewTab", 59 IDS_OPTIONS_STARTUP_SHOW_DEFAULT_AND_NEWTAB}, 60 { "startupShowLastSession", IDS_OPTIONS_STARTUP_SHOW_LAST_SESSION }, 61 { "startupShowPages", IDS_OPTIONS_STARTUP_SHOW_PAGES }, 62 { "startupAddLabel", IDS_OPTIONS_STARTUP_ADD_LABEL }, 63 { "startupUseCurrent", IDS_OPTIONS_STARTUP_USE_CURRENT }, 64 { "homepageGroupName", IDS_OPTIONS_HOMEPAGE_GROUP_NAME }, 65 { "homepageUseNewTab", IDS_OPTIONS_HOMEPAGE_USE_NEWTAB }, 66 { "homepageUseURL", IDS_OPTIONS_HOMEPAGE_USE_URL }, 67 { "toolbarGroupName", IDS_OPTIONS_TOOLBAR_GROUP_NAME }, 68 { "toolbarShowHomeButton", IDS_OPTIONS_TOOLBAR_SHOW_HOME_BUTTON }, 69 { "toolbarShowBookmarksBar", IDS_OPTIONS_TOOLBAR_SHOW_BOOKMARKS_BAR }, 70 { "defaultSearchGroupName", IDS_OPTIONS_DEFAULTSEARCH_GROUP_NAME }, 71 { "defaultSearchManageEngines", IDS_OPTIONS_DEFAULTSEARCH_MANAGE_ENGINES }, 72 { "instantName", IDS_INSTANT_PREF }, 73 { "instantWarningText", IDS_INSTANT_PREF_WARNING }, 74 { "instantConfirmTitle", IDS_INSTANT_OPT_IN_TITLE }, 75 { "instantConfirmMessage", IDS_INSTANT_OPT_IN_MESSAGE }, 76 { "defaultBrowserGroupName", IDS_OPTIONS_DEFAULTBROWSER_GROUP_NAME }, 77 }; 78 79 RegisterStrings(localized_strings, resources, arraysize(resources)); 80 RegisterTitle(localized_strings, "browserPage", 81 IDS_OPTIONS_GENERAL_TAB_LABEL); 82 83 localized_strings->SetString("instantLearnMoreLink", 84 ASCIIToUTF16(browser::InstantLearnMoreURL().spec())); 85 localized_strings->SetString("defaultBrowserUnknown", 86 l10n_util::GetStringFUTF16(IDS_OPTIONS_DEFAULTBROWSER_UNKNOWN, 87 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 88 localized_strings->SetString("defaultBrowserUseAsDefault", 89 l10n_util::GetStringFUTF16(IDS_OPTIONS_DEFAULTBROWSER_USEASDEFAULT, 90 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 91 } 92 93 void BrowserOptionsHandler::RegisterMessages() { 94 web_ui_->RegisterMessageCallback( 95 "setHomePage", 96 NewCallback(this, &BrowserOptionsHandler::SetHomePage)); 97 web_ui_->RegisterMessageCallback( 98 "becomeDefaultBrowser", 99 NewCallback(this, &BrowserOptionsHandler::BecomeDefaultBrowser)); 100 web_ui_->RegisterMessageCallback( 101 "setDefaultSearchEngine", 102 NewCallback(this, &BrowserOptionsHandler::SetDefaultSearchEngine)); 103 web_ui_->RegisterMessageCallback( 104 "removeStartupPages", 105 NewCallback(this, &BrowserOptionsHandler::RemoveStartupPages)); 106 web_ui_->RegisterMessageCallback( 107 "addStartupPage", 108 NewCallback(this, &BrowserOptionsHandler::AddStartupPage)); 109 web_ui_->RegisterMessageCallback( 110 "editStartupPage", 111 NewCallback(this, &BrowserOptionsHandler::EditStartupPage)); 112 web_ui_->RegisterMessageCallback( 113 "setStartupPagesToCurrentPages", 114 NewCallback(this, &BrowserOptionsHandler::SetStartupPagesToCurrentPages)); 115 web_ui_->RegisterMessageCallback( 116 "requestAutocompleteSuggestions", 117 NewCallback(this, 118 &BrowserOptionsHandler::RequestAutocompleteSuggestions)); 119 web_ui_->RegisterMessageCallback( 120 "toggleShowBookmarksBar", 121 NewCallback(this, &BrowserOptionsHandler::ToggleShowBookmarksBar)); 122 } 123 124 void BrowserOptionsHandler::Initialize() { 125 Profile* profile = web_ui_->GetProfile(); 126 127 // Create our favicon data source. 128 profile->GetChromeURLDataManager()->AddDataSource( 129 new FaviconSource(profile)); 130 131 homepage_.Init(prefs::kHomePage, profile->GetPrefs(), NULL); 132 default_browser_policy_.Init(prefs::kDefaultBrowserSettingEnabled, 133 g_browser_process->local_state(), 134 this); 135 UpdateDefaultBrowserState(); 136 UpdateStartupPages(); 137 UpdateSearchEngines(); 138 banner_handler_.reset( 139 new OptionsManagedBannerHandler(web_ui_, 140 ASCIIToUTF16("BrowserOptions"), 141 OPTIONS_PAGE_GENERAL)); 142 143 autocomplete_controller_.reset(new AutocompleteController(profile, this)); 144 } 145 146 void BrowserOptionsHandler::SetHomePage(const ListValue* args) { 147 std::string url_string; 148 std::string do_fixup_string; 149 int do_fixup; 150 CHECK_EQ(args->GetSize(), 2U); 151 CHECK(args->GetString(0, &url_string)); 152 CHECK(args->GetString(1, &do_fixup_string)); 153 CHECK(base::StringToInt(do_fixup_string, &do_fixup)); 154 155 if (do_fixup) { 156 GURL fixed_url = URLFixerUpper::FixupURL(url_string, std::string()); 157 url_string = fixed_url.spec(); 158 } 159 homepage_.SetValueIfNotManaged(url_string); 160 } 161 162 void BrowserOptionsHandler::UpdateDefaultBrowserState() { 163 #if defined(OS_WIN) 164 // Check for side-by-side first. 165 if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) { 166 SetDefaultBrowserUIString(IDS_OPTIONS_DEFAULTBROWSER_SXS); 167 return; 168 } 169 #endif 170 171 #if defined(OS_MACOSX) 172 ShellIntegration::DefaultBrowserState state = 173 ShellIntegration::IsDefaultBrowser(); 174 int status_string_id; 175 if (state == ShellIntegration::IS_DEFAULT_BROWSER) 176 status_string_id = IDS_OPTIONS_DEFAULTBROWSER_DEFAULT; 177 else if (state == ShellIntegration::NOT_DEFAULT_BROWSER) 178 status_string_id = IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT; 179 else 180 status_string_id = IDS_OPTIONS_DEFAULTBROWSER_UNKNOWN; 181 182 SetDefaultBrowserUIString(status_string_id); 183 #else 184 default_browser_worker_->StartCheckDefaultBrowser(); 185 #endif 186 } 187 188 void BrowserOptionsHandler::BecomeDefaultBrowser(const ListValue* args) { 189 // If the default browser setting is managed then we should not be able to 190 // call this function. 191 if (default_browser_policy_.IsManaged()) 192 return; 193 194 UserMetricsRecordAction(UserMetricsAction("Options_SetAsDefaultBrowser")); 195 #if defined(OS_MACOSX) 196 if (ShellIntegration::SetAsDefaultBrowser()) 197 UpdateDefaultBrowserState(); 198 #else 199 default_browser_worker_->StartSetAsDefaultBrowser(); 200 // Callback takes care of updating UI. 201 #endif 202 203 // If the user attempted to make Chrome the default browser, then he/she 204 // arguably wants to be notified when that changes. 205 PrefService* prefs = web_ui_->GetProfile()->GetPrefs(); 206 prefs->SetBoolean(prefs::kCheckDefaultBrowser, true); 207 } 208 209 int BrowserOptionsHandler::StatusStringIdForState( 210 ShellIntegration::DefaultBrowserState state) { 211 if (state == ShellIntegration::IS_DEFAULT_BROWSER) 212 return IDS_OPTIONS_DEFAULTBROWSER_DEFAULT; 213 if (state == ShellIntegration::NOT_DEFAULT_BROWSER) 214 return IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT; 215 return IDS_OPTIONS_DEFAULTBROWSER_UNKNOWN; 216 } 217 218 void BrowserOptionsHandler::SetDefaultBrowserUIState( 219 ShellIntegration::DefaultBrowserUIState state) { 220 int status_string_id; 221 if (state == ShellIntegration::STATE_IS_DEFAULT) 222 status_string_id = IDS_OPTIONS_DEFAULTBROWSER_DEFAULT; 223 else if (state == ShellIntegration::STATE_NOT_DEFAULT) 224 status_string_id = IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT; 225 else if (state == ShellIntegration::STATE_UNKNOWN) 226 status_string_id = IDS_OPTIONS_DEFAULTBROWSER_UNKNOWN; 227 else 228 return; // Still processing. 229 230 SetDefaultBrowserUIString(status_string_id); 231 } 232 233 void BrowserOptionsHandler::SetDefaultBrowserUIString(int status_string_id) { 234 scoped_ptr<Value> status_string(Value::CreateStringValue( 235 l10n_util::GetStringFUTF16(status_string_id, 236 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)))); 237 238 scoped_ptr<Value> is_default(Value::CreateBooleanValue( 239 status_string_id == IDS_OPTIONS_DEFAULTBROWSER_DEFAULT)); 240 241 scoped_ptr<Value> can_be_default(Value::CreateBooleanValue( 242 !default_browser_policy_.IsManaged() && 243 (status_string_id == IDS_OPTIONS_DEFAULTBROWSER_DEFAULT || 244 status_string_id == IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT))); 245 246 web_ui_->CallJavascriptFunction("BrowserOptions.updateDefaultBrowserState", 247 *(status_string.get()), 248 *(is_default.get()), 249 *(can_be_default.get())); 250 } 251 252 void BrowserOptionsHandler::OnTemplateURLModelChanged() { 253 if (!template_url_model_ || !template_url_model_->loaded()) 254 return; 255 256 const TemplateURL* default_url = 257 template_url_model_->GetDefaultSearchProvider(); 258 259 int default_index = 0; 260 ListValue search_engines; 261 std::vector<const TemplateURL*> model_urls = 262 template_url_model_->GetTemplateURLs(); 263 for (size_t i = 0; i < model_urls.size(); ++i) { 264 if (!model_urls[i]->ShowInDefaultList()) 265 continue; 266 267 DictionaryValue* entry = new DictionaryValue(); 268 entry->SetString("name", model_urls[i]->short_name()); 269 entry->SetInteger("index", i); 270 search_engines.Append(entry); 271 if (model_urls[i] == default_url) 272 default_index = i; 273 } 274 275 scoped_ptr<Value> default_value(Value::CreateIntegerValue(default_index)); 276 277 web_ui_->CallJavascriptFunction("BrowserOptions.updateSearchEngines", 278 search_engines, *(default_value.get())); 279 } 280 281 void BrowserOptionsHandler::SetDefaultSearchEngine(const ListValue* args) { 282 int selected_index = -1; 283 if (!ExtractIntegerValue(args, &selected_index)) { 284 NOTREACHED(); 285 return; 286 } 287 288 std::vector<const TemplateURL*> model_urls = 289 template_url_model_->GetTemplateURLs(); 290 if (selected_index >= 0 && 291 selected_index < static_cast<int>(model_urls.size())) 292 template_url_model_->SetDefaultSearchProvider(model_urls[selected_index]); 293 294 UserMetricsRecordAction(UserMetricsAction("Options_SearchEngineChanged")); 295 } 296 297 void BrowserOptionsHandler::UpdateSearchEngines() { 298 template_url_model_ = web_ui_->GetProfile()->GetTemplateURLModel(); 299 if (template_url_model_) { 300 template_url_model_->Load(); 301 template_url_model_->AddObserver(this); 302 OnTemplateURLModelChanged(); 303 } 304 } 305 306 void BrowserOptionsHandler::UpdateStartupPages() { 307 Profile* profile = web_ui_->GetProfile(); 308 startup_custom_pages_table_model_.reset( 309 new CustomHomePagesTableModel(profile)); 310 startup_custom_pages_table_model_->SetObserver(this); 311 312 const SessionStartupPref startup_pref = 313 SessionStartupPref::GetStartupPref(profile->GetPrefs()); 314 startup_custom_pages_table_model_->SetURLs(startup_pref.urls); 315 } 316 317 void BrowserOptionsHandler::OnModelChanged() { 318 ListValue startup_pages; 319 int page_count = startup_custom_pages_table_model_->RowCount(); 320 std::vector<GURL> urls = startup_custom_pages_table_model_->GetURLs(); 321 for (int i = 0; i < page_count; ++i) { 322 DictionaryValue* entry = new DictionaryValue(); 323 entry->SetString("title", startup_custom_pages_table_model_->GetText(i, 0)); 324 entry->SetString("url", urls[i].spec()); 325 entry->SetString("tooltip", 326 startup_custom_pages_table_model_->GetTooltip(i)); 327 entry->SetString("modelIndex", base::IntToString(i)); 328 startup_pages.Append(entry); 329 } 330 331 web_ui_->CallJavascriptFunction("BrowserOptions.updateStartupPages", 332 startup_pages); 333 } 334 335 void BrowserOptionsHandler::OnItemsChanged(int start, int length) { 336 OnModelChanged(); 337 } 338 339 void BrowserOptionsHandler::OnItemsAdded(int start, int length) { 340 OnModelChanged(); 341 } 342 343 void BrowserOptionsHandler::OnItemsRemoved(int start, int length) { 344 OnModelChanged(); 345 } 346 347 void BrowserOptionsHandler::Observe(NotificationType type, 348 const NotificationSource& source, 349 const NotificationDetails& details) { 350 UpdateDefaultBrowserState(); 351 } 352 353 void BrowserOptionsHandler::SetStartupPagesToCurrentPages( 354 const ListValue* args) { 355 startup_custom_pages_table_model_->SetToCurrentlyOpenPages(); 356 SaveStartupPagesPref(); 357 } 358 359 void BrowserOptionsHandler::RemoveStartupPages(const ListValue* args) { 360 for (int i = args->GetSize() - 1; i >= 0; --i) { 361 std::string string_value; 362 CHECK(args->GetString(i, &string_value)); 363 364 int selected_index; 365 base::StringToInt(string_value, &selected_index); 366 if (selected_index < 0 || 367 selected_index >= startup_custom_pages_table_model_->RowCount()) { 368 NOTREACHED(); 369 return; 370 } 371 startup_custom_pages_table_model_->Remove(selected_index); 372 } 373 374 SaveStartupPagesPref(); 375 } 376 377 void BrowserOptionsHandler::AddStartupPage(const ListValue* args) { 378 std::string url_string; 379 CHECK_EQ(args->GetSize(), 1U); 380 CHECK(args->GetString(0, &url_string)); 381 382 GURL url = URLFixerUpper::FixupURL(url_string, std::string()); 383 int index = startup_custom_pages_table_model_->RowCount(); 384 startup_custom_pages_table_model_->Add(index, url); 385 SaveStartupPagesPref(); 386 } 387 388 void BrowserOptionsHandler::EditStartupPage(const ListValue* args) { 389 std::string url_string; 390 std::string index_string; 391 int index; 392 CHECK_EQ(args->GetSize(), 2U); 393 CHECK(args->GetString(0, &index_string)); 394 CHECK(base::StringToInt(index_string, &index)); 395 CHECK(args->GetString(1, &url_string)); 396 397 if (index < 0 || index > startup_custom_pages_table_model_->RowCount()) { 398 NOTREACHED(); 399 return; 400 } 401 402 std::vector<GURL> urls = startup_custom_pages_table_model_->GetURLs(); 403 urls[index] = URLFixerUpper::FixupURL(url_string, std::string()); 404 startup_custom_pages_table_model_->SetURLs(urls); 405 } 406 407 void BrowserOptionsHandler::SaveStartupPagesPref() { 408 PrefService* prefs = web_ui_->GetProfile()->GetPrefs(); 409 410 SessionStartupPref pref = SessionStartupPref::GetStartupPref(prefs); 411 pref.urls = startup_custom_pages_table_model_->GetURLs(); 412 413 SessionStartupPref::SetStartupPref(prefs, pref); 414 } 415 416 void BrowserOptionsHandler::RequestAutocompleteSuggestions( 417 const ListValue* args) { 418 string16 input; 419 CHECK_EQ(args->GetSize(), 1U); 420 CHECK(args->GetString(0, &input)); 421 422 autocomplete_controller_->Start(input, string16(), true, false, false, 423 AutocompleteInput::ALL_MATCHES); 424 } 425 426 void BrowserOptionsHandler::ToggleShowBookmarksBar(const ListValue* args) { 427 Source<Profile> source(web_ui_->GetProfile()); 428 NotificationService::current()->Notify( 429 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, 430 source, 431 NotificationService::NoDetails()); 432 } 433 434 void BrowserOptionsHandler::OnResultChanged(bool default_match_changed) { 435 const AutocompleteResult& result = autocomplete_controller_->result(); 436 ListValue suggestions; 437 for (size_t i = 0; i < result.size(); ++i) { 438 const AutocompleteMatch& match = result.match_at(i); 439 AutocompleteMatch::Type type = match.type; 440 if (type != AutocompleteMatch::HISTORY_URL && 441 type != AutocompleteMatch::HISTORY_TITLE && 442 type != AutocompleteMatch::HISTORY_BODY && 443 type != AutocompleteMatch::HISTORY_KEYWORD && 444 type != AutocompleteMatch::NAVSUGGEST) 445 continue; 446 DictionaryValue* entry = new DictionaryValue(); 447 entry->SetString("title", match.description); 448 entry->SetString("displayURL", match.contents); 449 entry->SetString("url", match.destination_url.spec()); 450 suggestions.Append(entry); 451 } 452 453 web_ui_->CallJavascriptFunction( 454 "BrowserOptions.updateAutocompleteSuggestions", suggestions); 455 } 456