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