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