OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/views/tabs/tab.h" | 5 #include "chrome/browser/ui/views/tabs/tab.h" |
6 | 6 |
7 #include <limits> | 7 #include <limits> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/debug/alias.h" | 10 #include "base/debug/alias.h" |
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 | 335 |
336 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); | 336 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); |
337 } | 337 } |
338 | 338 |
339 Tab* tab_; | 339 Tab* tab_; |
340 | 340 |
341 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); | 341 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); |
342 }; | 342 }; |
343 | 343 |
344 //////////////////////////////////////////////////////////////////////////////// | 344 //////////////////////////////////////////////////////////////////////////////// |
| 345 // ThrobberView |
| 346 // |
| 347 // A Layer-backed view for updating a waiting or loading tab throbber. |
| 348 class Tab::ThrobberView : public views::View { |
| 349 public: |
| 350 explicit ThrobberView(Tab* owner); |
| 351 |
| 352 // Resets the times tracking when the throbber changes state. |
| 353 void ResetStartTimes(); |
| 354 |
| 355 private: |
| 356 // views::View: |
| 357 bool CanProcessEventsWithinSubtree() const override; |
| 358 void OnPaint(gfx::Canvas* canvas) override; |
| 359 |
| 360 Tab* owner_; // Weak. Owns |this|. |
| 361 |
| 362 // The point in time when the tab icon was first painted in the waiting state. |
| 363 base::TimeTicks waiting_start_time_; |
| 364 |
| 365 // The point in time when the tab icon was first painted in the loading state. |
| 366 base::TimeTicks loading_start_time_; |
| 367 |
| 368 // Paint state for the throbber after the most recent waiting paint. |
| 369 gfx::ThrobberWaitingState waiting_state_; |
| 370 |
| 371 DISALLOW_COPY_AND_ASSIGN(ThrobberView); |
| 372 }; |
| 373 |
| 374 Tab::ThrobberView::ThrobberView(Tab* owner) : owner_(owner) {} |
| 375 |
| 376 void Tab::ThrobberView::ResetStartTimes() { |
| 377 waiting_start_time_ = base::TimeTicks(); |
| 378 loading_start_time_ = base::TimeTicks(); |
| 379 waiting_state_ = gfx::ThrobberWaitingState(); |
| 380 } |
| 381 |
| 382 bool Tab::ThrobberView::CanProcessEventsWithinSubtree() const { |
| 383 return false; |
| 384 } |
| 385 |
| 386 void Tab::ThrobberView::OnPaint(gfx::Canvas* canvas) { |
| 387 const TabRendererData::NetworkState state = owner_->data().network_state; |
| 388 if (state == TabRendererData::NETWORK_STATE_NONE) |
| 389 return; |
| 390 |
| 391 ui::ThemeProvider* tp = GetThemeProvider(); |
| 392 const gfx::Rect bounds = GetLocalBounds(); |
| 393 if (state == TabRendererData::NETWORK_STATE_WAITING) { |
| 394 if (waiting_start_time_ == base::TimeTicks()) |
| 395 waiting_start_time_ = base::TimeTicks::Now(); |
| 396 |
| 397 waiting_state_.elapsed_time = base::TimeTicks::Now() - waiting_start_time_; |
| 398 gfx::PaintThrobberWaiting( |
| 399 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING), |
| 400 waiting_state_.elapsed_time); |
| 401 } else { |
| 402 if (loading_start_time_ == base::TimeTicks()) |
| 403 loading_start_time_ = base::TimeTicks::Now(); |
| 404 |
| 405 waiting_state_.color = |
| 406 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING); |
| 407 gfx::PaintThrobberSpinningAfterWaiting( |
| 408 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING), |
| 409 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); |
| 410 } |
| 411 } |
| 412 |
| 413 //////////////////////////////////////////////////////////////////////////////// |
345 // ImageCacheEntry | 414 // ImageCacheEntry |
346 | 415 |
347 Tab::ImageCacheEntry::ImageCacheEntry() | 416 Tab::ImageCacheEntry::ImageCacheEntry() |
348 : resource_id(-1), | 417 : resource_id(-1), |
349 scale_factor(ui::SCALE_FACTOR_NONE) { | 418 scale_factor(ui::SCALE_FACTOR_NONE) { |
350 } | 419 } |
351 | 420 |
352 Tab::ImageCacheEntry::~ImageCacheEntry() {} | 421 Tab::ImageCacheEntry::~ImageCacheEntry() {} |
353 | 422 |
354 //////////////////////////////////////////////////////////////////////////////// | 423 //////////////////////////////////////////////////////////////////////////////// |
(...skipping 10 matching lines...) Expand all Loading... |
365 // Tab, public: | 434 // Tab, public: |
366 | 435 |
367 Tab::Tab(TabController* controller) | 436 Tab::Tab(TabController* controller) |
368 : controller_(controller), | 437 : controller_(controller), |
369 closing_(false), | 438 closing_(false), |
370 dragging_(false), | 439 dragging_(false), |
371 detached_(false), | 440 detached_(false), |
372 favicon_hiding_offset_(0), | 441 favicon_hiding_offset_(0), |
373 immersive_loading_step_(0), | 442 immersive_loading_step_(0), |
374 should_display_crashed_favicon_(false), | 443 should_display_crashed_favicon_(false), |
| 444 throbber_(nullptr), |
375 media_indicator_button_(nullptr), | 445 media_indicator_button_(nullptr), |
376 close_button_(nullptr), | 446 close_button_(nullptr), |
377 title_(new views::Label()), | 447 title_(new views::Label()), |
378 tab_activated_with_last_tap_down_(false), | 448 tab_activated_with_last_tap_down_(false), |
379 hover_controller_(this), | 449 hover_controller_(this), |
380 showing_icon_(false), | 450 showing_icon_(false), |
381 showing_media_indicator_(false), | 451 showing_media_indicator_(false), |
382 showing_close_button_(false), | 452 showing_close_button_(false), |
383 button_color_(SK_ColorTRANSPARENT) { | 453 button_color_(SK_ColorTRANSPARENT) { |
384 DCHECK(controller); | 454 DCHECK(controller); |
385 InitTabResources(); | 455 InitTabResources(); |
386 | 456 |
387 // So we get don't get enter/exit on children and don't prematurely stop the | 457 // So we get don't get enter/exit on children and don't prematurely stop the |
388 // hover. | 458 // hover. |
389 set_notify_enter_exit_on_child(true); | 459 set_notify_enter_exit_on_child(true); |
390 | 460 |
391 set_id(VIEW_ID_TAB); | 461 set_id(VIEW_ID_TAB); |
392 | 462 |
393 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); | 463 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); |
394 title_->SetElideBehavior(gfx::FADE_TAIL); | 464 title_->SetElideBehavior(gfx::FADE_TAIL); |
395 title_->SetHandlesTooltips(false); | 465 title_->SetHandlesTooltips(false); |
396 title_->SetAutoColorReadabilityEnabled(false); | 466 title_->SetAutoColorReadabilityEnabled(false); |
397 title_->SetText(CoreTabHelper::GetDefaultTitle()); | 467 title_->SetText(CoreTabHelper::GetDefaultTitle()); |
398 AddChildView(title_); | 468 AddChildView(title_); |
399 | 469 |
400 SetEventTargeter( | 470 SetEventTargeter( |
401 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); | 471 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); |
402 | 472 |
| 473 throbber_ = new ThrobberView(this); |
| 474 throbber_->SetVisible(false); |
| 475 AddChildView(throbber_); |
| 476 |
403 media_indicator_button_ = new MediaIndicatorButton(this); | 477 media_indicator_button_ = new MediaIndicatorButton(this); |
404 AddChildView(media_indicator_button_); | 478 AddChildView(media_indicator_button_); |
405 | 479 |
406 close_button_ = new TabCloseButton(this); | 480 close_button_ = new TabCloseButton(this); |
407 close_button_->SetAccessibleName( | 481 close_button_->SetAccessibleName( |
408 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); | 482 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); |
409 // The normal image is set by OnButtonColorMaybeChanged() because it depends | 483 // The normal image is set by OnButtonColorMaybeChanged() because it depends |
410 // on the current theme and active state. The hovered and pressed images | 484 // on the current theme and active state. The hovered and pressed images |
411 // don't depend on the these, so we can set them here. | 485 // don't depend on the these, so we can set them here. |
412 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon( | 486 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon( |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
447 return controller_->IsTabSelected(this); | 521 return controller_->IsTabSelected(this); |
448 } | 522 } |
449 | 523 |
450 void Tab::SetData(const TabRendererData& data) { | 524 void Tab::SetData(const TabRendererData& data) { |
451 DCHECK(GetWidget()); | 525 DCHECK(GetWidget()); |
452 | 526 |
453 if (data_.Equals(data)) | 527 if (data_.Equals(data)) |
454 return; | 528 return; |
455 | 529 |
456 TabRendererData old(data_); | 530 TabRendererData old(data_); |
| 531 UpdateLoadingAnimation(data.network_state); |
457 data_ = data; | 532 data_ = data; |
458 | 533 |
459 base::string16 title = data_.title; | 534 base::string16 title = data_.title; |
460 if (title.empty()) { | 535 if (title.empty()) { |
461 title = data_.loading ? | 536 title = data_.loading ? |
462 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : | 537 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : |
463 CoreTabHelper::GetDefaultTitle(); | 538 CoreTabHelper::GetDefaultTitle(); |
464 } else { | 539 } else { |
465 Browser::FormatTitleForDisplay(&title); | 540 Browser::FormatTitleForDisplay(&title); |
466 } | 541 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 } | 581 } |
507 | 582 |
508 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { | 583 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { |
509 if (state == data_.network_state && | 584 if (state == data_.network_state && |
510 state == TabRendererData::NETWORK_STATE_NONE) { | 585 state == TabRendererData::NETWORK_STATE_NONE) { |
511 // If the network state is none and hasn't changed, do nothing. Otherwise we | 586 // If the network state is none and hasn't changed, do nothing. Otherwise we |
512 // need to advance the animation frame. | 587 // need to advance the animation frame. |
513 return; | 588 return; |
514 } | 589 } |
515 | 590 |
516 TabRendererData::NetworkState old_state = data_.network_state; | |
517 data_.network_state = state; | 591 data_.network_state = state; |
518 AdvanceLoadingAnimation(old_state, state); | 592 AdvanceLoadingAnimation(); |
519 } | 593 } |
520 | 594 |
521 void Tab::StartPulse() { | 595 void Tab::StartPulse() { |
522 pulse_animation_.reset(new gfx::ThrobAnimation(this)); | 596 pulse_animation_.reset(new gfx::ThrobAnimation(this)); |
523 pulse_animation_->SetSlideDuration(kPulseDurationMs); | 597 pulse_animation_->SetSlideDuration(kPulseDurationMs); |
524 if (animation_container_.get()) | 598 if (animation_container_.get()) |
525 pulse_animation_->SetContainer(animation_container_.get()); | 599 pulse_animation_->SetContainer(animation_container_.get()); |
526 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); | 600 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); |
527 } | 601 } |
528 | 602 |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
788 const int extra_padding = | 862 const int extra_padding = |
789 (controller_->ShouldHideCloseButtonForInactiveTabs() || | 863 (controller_->ShouldHideCloseButtonForInactiveTabs() || |
790 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; | 864 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; |
791 const int start = lb.x() + extra_padding; | 865 const int start = lb.x() + extra_padding; |
792 favicon_bounds_.SetRect(start, lb.y(), 0, 0); | 866 favicon_bounds_.SetRect(start, lb.y(), 0, 0); |
793 if (showing_icon_) { | 867 if (showing_icon_) { |
794 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); | 868 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); |
795 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); | 869 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); |
796 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); | 870 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); |
797 } | 871 } |
| 872 throbber_->SetBoundsRect(favicon_bounds_); |
798 | 873 |
799 showing_close_button_ = ShouldShowCloseBox(); | 874 showing_close_button_ = ShouldShowCloseBox(); |
800 if (showing_close_button_) { | 875 if (showing_close_button_) { |
801 // If the ratio of the close button size to tab width exceeds the maximum. | 876 // If the ratio of the close button size to tab width exceeds the maximum. |
802 // The close button should be as large as possible so that there is a larger | 877 // The close button should be as large as possible so that there is a larger |
803 // hit-target for touch events. So the close button bounds extends to the | 878 // hit-target for touch events. So the close button bounds extends to the |
804 // edges of the tab. However, the larger hit-target should be active only | 879 // edges of the tab. However, the larger hit-target should be active only |
805 // for mouse events, and the close-image should show up in the right place. | 880 // for mouse events, and the close-image should show up in the right place. |
806 // So a border is added to the button with necessary padding. The close | 881 // So a border is added to the button with necessary padding. The close |
807 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target | 882 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target |
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1344 | 1419 |
1345 void Tab::PaintIcon(gfx::Canvas* canvas) { | 1420 void Tab::PaintIcon(gfx::Canvas* canvas) { |
1346 gfx::Rect bounds = favicon_bounds_; | 1421 gfx::Rect bounds = favicon_bounds_; |
1347 bounds.set_x(GetMirroredXForRect(bounds)); | 1422 bounds.set_x(GetMirroredXForRect(bounds)); |
1348 bounds.Offset(0, favicon_hiding_offset_); | 1423 bounds.Offset(0, favicon_hiding_offset_); |
1349 bounds.Intersect(GetInteriorBounds()); | 1424 bounds.Intersect(GetInteriorBounds()); |
1350 if (bounds.IsEmpty()) | 1425 if (bounds.IsEmpty()) |
1351 return; | 1426 return; |
1352 | 1427 |
1353 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { | 1428 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { |
1354 // Paint network activity (aka throbber) animation frame. | 1429 // Throbber will do its own painting. |
1355 ui::ThemeProvider* tp = GetThemeProvider(); | |
1356 if (data().network_state == TabRendererData::NETWORK_STATE_WAITING) { | |
1357 if (waiting_start_time_ == base::TimeTicks()) | |
1358 waiting_start_time_ = base::TimeTicks::Now(); | |
1359 | |
1360 waiting_state_.elapsed_time = | |
1361 base::TimeTicks::Now() - waiting_start_time_; | |
1362 gfx::PaintThrobberWaiting( | |
1363 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING), | |
1364 waiting_state_.elapsed_time); | |
1365 } else { | |
1366 if (loading_start_time_ == base::TimeTicks()) | |
1367 loading_start_time_ = base::TimeTicks::Now(); | |
1368 | |
1369 waiting_state_.color = | |
1370 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING); | |
1371 gfx::PaintThrobberSpinningAfterWaiting( | |
1372 canvas, bounds, | |
1373 tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING), | |
1374 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); | |
1375 } | |
1376 } else { | 1430 } else { |
1377 const gfx::ImageSkia& favicon = should_display_crashed_favicon_ ? | 1431 const gfx::ImageSkia& favicon = should_display_crashed_favicon_ ? |
1378 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( | 1432 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
1379 IDR_CRASH_SAD_FAVICON) : | 1433 IDR_CRASH_SAD_FAVICON) : |
1380 data().favicon; | 1434 data().favicon; |
1381 if (!favicon.isNull()) { | 1435 if (!favicon.isNull()) { |
1382 canvas->DrawImageInt(favicon, 0, 0, bounds.width(), bounds.height(), | 1436 canvas->DrawImageInt(favicon, 0, 0, bounds.width(), bounds.height(), |
1383 bounds.x(), bounds.y(), bounds.width(), | 1437 bounds.x(), bounds.y(), bounds.width(), |
1384 bounds.height(), false); | 1438 bounds.height(), false); |
1385 } | 1439 } |
1386 } | 1440 } |
1387 } | 1441 } |
1388 | 1442 |
1389 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, | 1443 void Tab::AdvanceLoadingAnimation() { |
1390 TabRendererData::NetworkState state) { | 1444 const TabRendererData::NetworkState state = data().network_state; |
1391 if (state == TabRendererData::NETWORK_STATE_WAITING) { | 1445 if (controller_->IsImmersiveStyle()) { |
1392 // Waiting steps backwards. | 1446 if (state == TabRendererData::NETWORK_STATE_WAITING) { |
1393 immersive_loading_step_ = | 1447 // Waiting steps backwards. |
1394 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % | 1448 immersive_loading_step_ = |
1395 kImmersiveLoadingStepCount; | 1449 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % |
1396 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { | 1450 kImmersiveLoadingStepCount; |
1397 immersive_loading_step_ = (immersive_loading_step_ + 1) % | 1451 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { |
1398 kImmersiveLoadingStepCount; | 1452 immersive_loading_step_ = |
1399 } else { | 1453 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount; |
1400 waiting_start_time_ = base::TimeTicks(); | 1454 } else { |
1401 loading_start_time_ = base::TimeTicks(); | 1455 immersive_loading_step_ = 0; |
1402 waiting_state_ = gfx::ThrobberWaitingState(); | 1456 } |
1403 immersive_loading_step_ = 0; | 1457 |
| 1458 SchedulePaintInRect(GetImmersiveBarRect()); |
| 1459 return; |
1404 } | 1460 } |
1405 if (controller_->IsImmersiveStyle()) { | 1461 |
1406 SchedulePaintInRect(GetImmersiveBarRect()); | 1462 if (state == TabRendererData::NETWORK_STATE_NONE) { |
1407 } else { | 1463 throbber_->ResetStartTimes(); |
| 1464 throbber_->SetVisible(false); |
1408 ScheduleIconPaint(); | 1465 ScheduleIconPaint(); |
| 1466 return; |
1409 } | 1467 } |
| 1468 |
| 1469 // Since the throbber can animate for a long time, paint to a separate layer |
| 1470 // when possible to reduce repaint overhead. |
| 1471 const bool paint_to_layer = controller_->CanPaintThrobberToLayer(); |
| 1472 if (paint_to_layer != !!throbber_->layer()) { |
| 1473 throbber_->SetPaintToLayer(paint_to_layer); |
| 1474 throbber_->SetFillsBoundsOpaquely(false); |
| 1475 if (paint_to_layer) |
| 1476 ScheduleIconPaint(); // Ensure the non-layered throbber goes away. |
| 1477 } |
| 1478 if (!throbber_->visible()) { |
| 1479 ScheduleIconPaint(); // Repaint the icon area to hide the favicon. |
| 1480 throbber_->SetVisible(true); |
| 1481 } |
| 1482 throbber_->SchedulePaint(); |
1410 } | 1483 } |
1411 | 1484 |
1412 int Tab::IconCapacity() const { | 1485 int Tab::IconCapacity() const { |
1413 const gfx::Size min_size(GetMinimumInactiveSize()); | 1486 const gfx::Size min_size(GetMinimumInactiveSize()); |
1414 if (height() < min_size.height()) | 1487 if (height() < min_size.height()) |
1415 return 0; | 1488 return 0; |
1416 const int available_width = std::max(0, width() - min_size.width()); | 1489 const int available_width = std::max(0, width() - min_size.width()); |
1417 // All icons are the same size as the favicon. | 1490 // All icons are the same size as the favicon. |
1418 const int icon_width = gfx::kFaviconSize; | 1491 const int icon_width = gfx::kFaviconSize; |
1419 // We need enough space to display the icons flush against each other. | 1492 // We need enough space to display the icons flush against each other. |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1611 const gfx::ImageSkia& image) { | 1684 const gfx::ImageSkia& image) { |
1612 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); | 1685 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); |
1613 ImageCacheEntry entry; | 1686 ImageCacheEntry entry; |
1614 entry.resource_id = resource_id; | 1687 entry.resource_id = resource_id; |
1615 entry.scale_factor = scale_factor; | 1688 entry.scale_factor = scale_factor; |
1616 entry.image = image; | 1689 entry.image = image; |
1617 image_cache_->push_front(entry); | 1690 image_cache_->push_front(entry); |
1618 if (image_cache_->size() > kMaxImageCacheSize) | 1691 if (image_cache_->size() > kMaxImageCacheSize) |
1619 image_cache_->pop_back(); | 1692 image_cache_->pop_back(); |
1620 } | 1693 } |
OLD | NEW |