eula_view.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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/chromeos/login/eula_view.h" 6 7#include <signal.h> 8#include <sys/types.h> 9#include <string> 10 11#include "app/l10n_util.h" 12#include "app/resource_bundle.h" 13#include "base/basictypes.h" 14#include "base/message_loop.h" 15#include "base/task.h" 16#include "base/utf_string_conversions.h" 17#include "base/values.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/chromeos/cros/cros_library.h" 20#include "chrome/browser/chromeos/cros/cryptohome_library.h" 21#include "chrome/browser/chromeos/customization_document.h" 22#include "chrome/browser/chromeos/login/help_app_launcher.h" 23#include "chrome/browser/chromeos/login/helper.h" 24#include "chrome/browser/chromeos/login/network_screen_delegate.h" 25#include "chrome/browser/chromeos/login/rounded_rect_painter.h" 26#include "chrome/browser/chromeos/login/wizard_controller.h" 27#include "chrome/browser/chromeos/metrics_cros_settings_provider.h" 28#include "chrome/browser/profile_manager.h" 29#include "chrome/browser/renderer_host/site_instance.h" 30#include "chrome/browser/tab_contents/tab_contents.h" 31#include "chrome/browser/views/dom_view.h" 32#include "chrome/browser/views/window.h" 33#include "chrome/common/native_web_keyboard_event.h" 34#include "chrome/common/url_constants.h" 35#include "grit/chromium_strings.h" 36#include "grit/generated_resources.h" 37#include "grit/locale_settings.h" 38#include "grit/theme_resources.h" 39#include "views/controls/button/checkbox.h" 40#include "views/controls/button/native_button.h" 41#include "views/controls/label.h" 42#include "views/controls/throbber.h" 43#include "views/grid_layout.h" 44#include "views/layout_manager.h" 45#include "views/standard_layout.h" 46#include "views/widget/widget_gtk.h" 47#include "views/window/dialog_delegate.h" 48#include "views/window/window.h" 49 50using views::WidgetGtk; 51 52namespace { 53 54const int kBorderSize = 10; 55const int kCheckBowWidth = 22; 56const int kLastButtonHorizontalMargin = 10; 57const int kMargin = 20; 58const int kTextMargin = 10; 59const int kTpmCheckIntervalMs = 500; 60 61// TODO(glotov): this URL should be changed to actual Google ChromeOS EULA. 62// See crbug.com/4647 63const char kGoogleEulaUrl[] = "about:terms"; 64 65enum kLayoutColumnsets { 66 SINGLE_CONTROL_ROW, 67 SINGLE_CONTROL_WITH_SHIFT_ROW, 68 SINGLE_LINK_WITH_SHIFT_ROW, 69 LAST_ROW 70}; 71 72// A simple LayoutManager that causes the associated view's one child to be 73// sized to match the bounds of its parent except the bounds, if set. 74struct FillLayoutWithBorder : public views::LayoutManager { 75 // Overridden from LayoutManager: 76 virtual void Layout(views::View* host) { 77 DCHECK(host->GetChildViewCount()); 78 host->GetChildViewAt(0)->SetBounds(host->GetLocalBounds(false)); 79 } 80 virtual gfx::Size GetPreferredSize(views::View* host) { 81 return gfx::Size(host->width(), host->height()); 82 } 83}; 84 85// System security setting dialog. 86class TpmInfoView : public views::View, 87 public views::DialogDelegate { 88 public: 89 explicit TpmInfoView(std::string* password) 90 : ALLOW_THIS_IN_INITIALIZER_LIST(runnable_method_factory_(this)), 91 password_(password) { 92 DCHECK(password_); 93 } 94 95 void Init(); 96 97 protected: 98 // views::DialogDelegate overrides: 99 virtual bool Accept() { return true; } 100 virtual bool IsModal() const { return true; } 101 virtual views::View* GetContentsView() { return this; } 102 virtual int GetDialogButtons() const { 103 return MessageBoxFlags::DIALOGBUTTON_OK; 104 } 105 106 // views::View overrides: 107 virtual std::wstring GetWindowTitle() const { 108 return l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTING); 109 } 110 111 gfx::Size GetPreferredSize() { 112 return gfx::Size(views::Window::GetLocalizedContentsSize( 113 IDS_TPM_INFO_DIALOG_WIDTH_CHARS, 114 IDS_TPM_INFO_DIALOG_HEIGHT_LINES)); 115 } 116 117 private: 118 void PullPassword(); 119 120 ScopedRunnableMethodFactory<TpmInfoView> runnable_method_factory_; 121 122 // Holds pointer to the password storage. 123 std::string* password_; 124 125 views::Label* busy_label_; 126 views::Label* password_label_; 127 views::Throbber* throbber_; 128 129 DISALLOW_COPY_AND_ASSIGN(TpmInfoView); 130}; 131 132void TpmInfoView::Init() { 133 views::GridLayout* layout = CreatePanelGridLayout(this); 134 SetLayoutManager(layout); 135 views::ColumnSet* column_set = layout->AddColumnSet(0); 136 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, 137 views::GridLayout::USE_PREF, 0, 0); 138 layout->StartRow(0, 0); 139 views::Label* label = new views::Label( 140 l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTING_DESCRIPTION)); 141 label->SetMultiLine(true); 142 label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); 143 layout->AddView(label); 144 layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); 145 146 layout->StartRow(0, 0); 147 label = new views::Label( 148 l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTING_DESCRIPTION_KEY)); 149 label->SetMultiLine(true); 150 label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); 151 layout->AddView(label); 152 layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); 153 154 column_set = layout->AddColumnSet(1); 155 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, 156 views::GridLayout::USE_PREF, 0, 0); 157 layout->StartRow(0, 1); 158 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 159 gfx::Font password_font = 160 rb.GetFont(ResourceBundle::MediumFont).DeriveFont(0, gfx::Font::BOLD); 161 // Password will be set later. 162 password_label_ = new views::Label(L"", password_font); 163 password_label_->SetVisible(false); 164 layout->AddView(password_label_); 165 layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); 166 167 column_set = layout->AddColumnSet(2); 168 column_set->AddPaddingColumn(1, 0); 169 // Resize of the throbber and label is not allowed, since we want they to be 170 // placed in the center. 171 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 172 views::GridLayout::USE_PREF, 0, 0); 173 column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing); 174 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 175 views::GridLayout::USE_PREF, 0, 0); 176 column_set->AddPaddingColumn(1, 0); 177 // Border padding columns should have the same width. It guaranties that 178 // throbber and label will be placed in the center. 179 column_set->LinkColumnSizes(0, 4, -1); 180 181 layout->StartRow(0, 2); 182 throbber_ = chromeos::CreateDefaultThrobber(); 183 throbber_->Start(); 184 layout->AddView(throbber_); 185 busy_label_ = new views::Label(l10n_util::GetString(IDS_EULA_TPM_BUSY)); 186 layout->AddView(busy_label_); 187 layout->AddPaddingRow(0, kRelatedControlHorizontalSpacing); 188 189 PullPassword(); 190} 191 192void TpmInfoView::PullPassword() { 193 // Since this method is also called directly. 194 runnable_method_factory_.RevokeAll(); 195 196 chromeos::CryptohomeLibrary* cryptohome = 197 chromeos::CrosLibrary::Get()->GetCryptohomeLibrary(); 198 199 bool password_acquired = false; 200 if (password_->empty() && cryptohome->TpmIsReady()) { 201 password_acquired = cryptohome->TpmGetPassword(password_); 202 if (!password_acquired) { 203 password_->clear(); 204 } else if (password_->empty()) { 205 // For a fresh OOBE flow TPM is uninitialized, 206 // ownership process is started at the EULA screen, 207 // password is cleared after EULA is accepted. 208 LOG(ERROR) << "TPM returned an empty password."; 209 } 210 } 211 if (password_->empty() && !password_acquired) { 212 // Password hasn't been acquired, reschedule pulling. 213 MessageLoop::current()->PostDelayedTask( 214 FROM_HERE, 215 runnable_method_factory_.NewRunnableMethod(&TpmInfoView::PullPassword), 216 kTpmCheckIntervalMs); 217 } else { 218 password_label_->SetText(ASCIIToWide(*password_)); 219 password_label_->SetVisible(true); 220 busy_label_->SetVisible(false); 221 throbber_->Stop(); 222 throbber_->SetVisible(false); 223 } 224} 225 226} // namespace 227 228namespace chromeos { 229 230//////////////////////////////////////////////////////////////////////////////// 231// EulaView, public: 232 233EulaView::EulaView(chromeos::ScreenObserver* observer) 234 : google_eula_label_(NULL), 235 google_eula_view_(NULL), 236 usage_statistics_checkbox_(NULL), 237 learn_more_link_(NULL), 238 oem_eula_label_(NULL), 239 oem_eula_view_(NULL), 240 system_security_settings_link_(NULL), 241 back_button_(NULL), 242 continue_button_(NULL), 243 observer_(observer), 244 bubble_(NULL) { 245} 246 247EulaView::~EulaView() { 248 // bubble_ will be set to NULL in callback. 249 if (bubble_) 250 bubble_->Close(); 251} 252 253// Convenience function to set layout's columnsets for this screen. 254static void SetUpGridLayout(views::GridLayout* layout) { 255 static const int kPadding = kBorderSize + kMargin; 256 views::ColumnSet* column_set = layout->AddColumnSet(SINGLE_CONTROL_ROW); 257 column_set->AddPaddingColumn(0, kPadding); 258 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, 259 views::GridLayout::USE_PREF, 0, 0); 260 column_set->AddPaddingColumn(0, kPadding); 261 262 column_set = layout->AddColumnSet(SINGLE_CONTROL_WITH_SHIFT_ROW); 263 column_set->AddPaddingColumn(0, kPadding + kTextMargin); 264 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, 265 views::GridLayout::USE_PREF, 0, 0); 266 column_set->AddPaddingColumn(0, kPadding); 267 268 column_set = layout->AddColumnSet(SINGLE_LINK_WITH_SHIFT_ROW); 269 column_set->AddPaddingColumn(0, kPadding + kTextMargin + kCheckBowWidth); 270 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, 271 views::GridLayout::USE_PREF, 0, 0); 272 column_set->AddPaddingColumn(0, kPadding); 273 274 column_set = layout->AddColumnSet(LAST_ROW); 275 column_set->AddPaddingColumn(0, kPadding + kTextMargin); 276 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, 277 views::GridLayout::USE_PREF, 0, 0); 278 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 279 views::GridLayout::USE_PREF, 0, 0); 280 column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing); 281 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 282 views::GridLayout::USE_PREF, 0, 0); 283 column_set->AddPaddingColumn(0, kLastButtonHorizontalMargin + kBorderSize); 284} 285 286// Convenience function. Returns URL of the OEM EULA page that should be 287// displayed using current locale and manifest. Returns empty URL otherwise. 288static GURL GetOemEulaPagePath() { 289 const StartupCustomizationDocument *customization = 290 WizardController::default_controller()->GetCustomization(); 291 if (customization) { 292 std::string locale = g_browser_process->GetApplicationLocale(); 293 FilePath eula_page_path = customization->GetEULAPagePath(locale); 294 if (eula_page_path.empty()) { 295 VLOG(1) << "No eula found for locale: " << locale; 296 locale = customization->initial_locale(); 297 eula_page_path = customization->GetEULAPagePath(locale); 298 } 299 if (!eula_page_path.empty()) { 300 const std::string page_path = std::string(chrome::kFileScheme) + 301 chrome::kStandardSchemeSeparator + eula_page_path.value(); 302 return GURL(page_path); 303 } else { 304 VLOG(1) << "No eula found for locale: " << locale; 305 } 306 } else { 307 LOG(ERROR) << "No manifest found."; 308 } 309 return GURL(); 310} 311 312void EulaView::Init() { 313 // First, command to own the TPM. 314 if (chromeos::CrosLibrary::Get()->EnsureLoaded()) { 315 chromeos::CrosLibrary::Get()-> 316 GetCryptohomeLibrary()->TpmCanAttemptOwnership(); 317 } else { 318 LOG(ERROR) << "Cros library not loaded. " 319 << "We must have disabled the link that led here."; 320 } 321 322 // Use rounded rect background. 323 views::Painter* painter = CreateWizardPainter( 324 &BorderDefinition::kScreenBorder); 325 set_background( 326 views::Background::CreateBackgroundPainter(true, painter)); 327 328 // Layout created controls. 329 views::GridLayout* layout = new views::GridLayout(this); 330 SetLayoutManager(layout); 331 SetUpGridLayout(layout); 332 333 static const int kPadding = kBorderSize + kMargin; 334 layout->AddPaddingRow(0, kPadding); 335 layout->StartRow(0, SINGLE_CONTROL_ROW); 336 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 337 gfx::Font label_font = 338 rb.GetFont(ResourceBundle::MediumFont).DeriveFont(0, gfx::Font::NORMAL); 339 google_eula_label_ = new views::Label(std::wstring(), label_font); 340 layout->AddView(google_eula_label_, 1, 1, 341 views::GridLayout::LEADING, views::GridLayout::FILL); 342 343 layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); 344 layout->StartRow(1, SINGLE_CONTROL_ROW); 345 views::View* box_view = new views::View(); 346 box_view->set_border(views::Border::CreateSolidBorder(1, SK_ColorBLACK)); 347 box_view->SetLayoutManager(new FillLayoutWithBorder()); 348 layout->AddView(box_view); 349 350 google_eula_view_ = new DOMView(); 351 box_view->AddChildView(google_eula_view_); 352 353 layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); 354 layout->StartRow(0, SINGLE_CONTROL_WITH_SHIFT_ROW); 355 usage_statistics_checkbox_ = new views::Checkbox(); 356 usage_statistics_checkbox_->SetMultiLine(true); 357 usage_statistics_checkbox_->SetChecked(true); 358 layout->AddView(usage_statistics_checkbox_); 359 360 layout->StartRow(0, SINGLE_LINK_WITH_SHIFT_ROW); 361 learn_more_link_ = new views::Link(); 362 learn_more_link_->SetController(this); 363 layout->AddView(learn_more_link_); 364 365 layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); 366 layout->StartRow(0, SINGLE_CONTROL_ROW); 367 oem_eula_label_ = new views::Label(std::wstring(), label_font); 368 layout->AddView(oem_eula_label_, 1, 1, 369 views::GridLayout::LEADING, views::GridLayout::FILL); 370 371 oem_eula_page_ = GetOemEulaPagePath(); 372 if (!oem_eula_page_.is_empty()) { 373 layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); 374 layout->StartRow(1, SINGLE_CONTROL_ROW); 375 box_view = new views::View(); 376 box_view->SetLayoutManager(new FillLayoutWithBorder()); 377 box_view->set_border(views::Border::CreateSolidBorder(1, SK_ColorBLACK)); 378 layout->AddView(box_view); 379 380 oem_eula_view_ = new DOMView(); 381 box_view->AddChildView(oem_eula_view_); 382 } 383 384 layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); 385 layout->StartRow(0, LAST_ROW); 386 system_security_settings_link_ = new views::Link(); 387 system_security_settings_link_->SetController(this); 388 389 if (!chromeos::CrosLibrary::Get()->EnsureLoaded() || 390 !chromeos::CrosLibrary::Get()->GetCryptohomeLibrary()-> 391 TpmIsEnabled()) { 392 system_security_settings_link_->SetEnabled(false); 393 } 394 395 layout->AddView(system_security_settings_link_); 396 397 back_button_ = new views::NativeButton(this, std::wstring()); 398 layout->AddView(back_button_); 399 400 continue_button_ = new views::NativeButton(this, std::wstring()); 401 layout->AddView(continue_button_); 402 layout->AddPaddingRow(0, kPadding); 403 404 UpdateLocalizedStrings(); 405} 406 407void EulaView::UpdateLocalizedStrings() { 408 // Load Google EULA and its title. 409 LoadEulaView(google_eula_view_, google_eula_label_, GURL(kGoogleEulaUrl)); 410 411 // Load OEM EULA and its title. 412 if (!oem_eula_page_.is_empty()) 413 LoadEulaView(oem_eula_view_, oem_eula_label_, oem_eula_page_); 414 415 // Set tooltip for usage statistics checkbox if the metric is unmanaged. 416 if (!usage_statistics_checkbox_->IsEnabled()) { 417 usage_statistics_checkbox_->SetTooltipText( 418 l10n_util::GetString(IDS_OPTION_DISABLED_BY_POLICY)); 419 } 420 421 // Set tooltip for system security settings link if TPM is disabled. 422 if (!system_security_settings_link_->IsEnabled()) { 423 system_security_settings_link_->SetTooltipText( 424 l10n_util::GetString(IDS_EULA_TPM_DISABLED)); 425 } 426 427 // Load other labels from resources. 428 usage_statistics_checkbox_->SetLabel( 429 l10n_util::GetString(IDS_EULA_CHECKBOX_ENABLE_LOGGING)); 430 learn_more_link_->SetText( 431 l10n_util::GetString(IDS_LEARN_MORE)); 432 system_security_settings_link_->SetText( 433 l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTING)); 434 continue_button_->SetLabel( 435 l10n_util::GetString(IDS_EULA_ACCEPT_AND_CONTINUE_BUTTON)); 436 back_button_->SetLabel( 437 l10n_util::GetString(IDS_EULA_BACK_BUTTON)); 438} 439 440//////////////////////////////////////////////////////////////////////////////// 441// EulaView, protected, views::View implementation: 442 443void EulaView::OnLocaleChanged() { 444 UpdateLocalizedStrings(); 445 Layout(); 446} 447 448//////////////////////////////////////////////////////////////////////////////// 449// views::ButtonListener implementation: 450 451void EulaView::ButtonPressed(views::Button* sender, const views::Event& event) { 452 if (sender == continue_button_) { 453 if (usage_statistics_checkbox_) { 454 MetricsCrosSettingsProvider::SetMetricsStatus( 455 usage_statistics_checkbox_->checked()); 456 } 457 observer_->OnExit(ScreenObserver::EULA_ACCEPTED); 458 } else if (sender == back_button_) { 459 observer_->OnExit(ScreenObserver::EULA_BACK); 460 } 461} 462 463//////////////////////////////////////////////////////////////////////////////// 464// views::LinkController implementation: 465 466void EulaView::LinkActivated(views::Link* source, int event_flags) { 467 if (source == learn_more_link_) { 468 if (!help_app_.get()) 469 help_app_.reset(new HelpAppLauncher(GetNativeWindow())); 470 help_app_->ShowHelpTopic(HelpAppLauncher::HELP_STATS_USAGE); 471 } else if (source == system_security_settings_link_) { 472 TpmInfoView* view = new TpmInfoView(&tpm_password_); 473 view->Init(); 474 views::Window* window = browser::CreateViewsWindow( 475 GetNativeWindow(), gfx::Rect(), view); 476 window->SetIsAlwaysOnTop(true); 477 window->Show(); 478 } 479} 480 481//////////////////////////////////////////////////////////////////////////////// 482// TabContentsDelegate implementation: 483 484// Convenience function. Queries |eula_view| for HTML title and, if it 485// is ready, assigns it to |eula_label| and returns true so the caller 486// view calls Layout(). 487static bool PublishTitleIfReady(const TabContents* contents, 488 DOMView* eula_view, 489 views::Label* eula_label) { 490 if (contents != eula_view->tab_contents()) 491 return false; 492 eula_label->SetText(UTF16ToWide(eula_view->tab_contents()->GetTitle())); 493 return true; 494} 495 496void EulaView::NavigationStateChanged(const TabContents* contents, 497 unsigned changed_flags) { 498 if (changed_flags & TabContents::INVALIDATE_TITLE) { 499 if (PublishTitleIfReady(contents, google_eula_view_, google_eula_label_) || 500 PublishTitleIfReady(contents, oem_eula_view_, oem_eula_label_)) { 501 Layout(); 502 } 503 } 504} 505 506void EulaView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { 507 views::Widget* widget = GetWidget(); 508 if (widget && event.os_event && !event.skip_in_browser) 509 static_cast<views::WidgetGtk*>(widget)->HandleKeyboardEvent(event.os_event); 510} 511 512//////////////////////////////////////////////////////////////////////////////// 513// EulaView, private: 514 515gfx::NativeWindow EulaView::GetNativeWindow() const { 516 return GTK_WINDOW(static_cast<WidgetGtk*>(GetWidget())->GetNativeView()); 517} 518 519void EulaView::LoadEulaView(DOMView* eula_view, 520 views::Label* eula_label, 521 const GURL& eula_url) { 522 Profile* profile = ProfileManager::GetDefaultProfile(); 523 eula_view->Init(profile, 524 SiteInstance::CreateSiteInstanceForURL(profile, eula_url)); 525 eula_view->LoadURL(eula_url); 526 eula_view->tab_contents()->set_delegate(this); 527} 528 529//////////////////////////////////////////////////////////////////////////////// 530// EulaView, private, views::View implementation: 531 532bool EulaView::OnKeyPressed(const views::KeyEvent&) { 533 // Close message bubble if shown. bubble_ will be set to NULL in callback. 534 if (bubble_) { 535 bubble_->Close(); 536 return true; 537 } 538 return false; 539} 540 541} // namespace chromeos 542