| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 #include <limits> | 8 #include <limits> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 | 109 |
| 110 // The minimum opacity (out of 1) when a tab (either active or inactive) is | 110 // The minimum opacity (out of 1) when a tab (either active or inactive) is |
| 111 // throbbing in the immersive mode light strip. | 111 // throbbing in the immersive mode light strip. |
| 112 const double kImmersiveTabMinThrobOpacity = 0.66; | 112 const double kImmersiveTabMinThrobOpacity = 0.66; |
| 113 | 113 |
| 114 // Number of steps in the immersive mode loading animation. | 114 // Number of steps in the immersive mode loading animation. |
| 115 const int kImmersiveLoadingStepCount = 32; | 115 const int kImmersiveLoadingStepCount = 32; |
| 116 | 116 |
| 117 const char kTabCloseButtonName[] = "TabCloseButton"; | 117 const char kTabCloseButtonName[] = "TabCloseButton"; |
| 118 | 118 |
| 119 // Desaturate favicon HSL shift values. | |
| 120 const double kDesaturateHue = -1.0; | |
| 121 const double kDesaturateSaturation = 0.0; | |
| 122 const double kDesaturateLightness = 0.6; | |
| 123 | |
| 124 //////////////////////////////////////////////////////////////////////////////// | 119 //////////////////////////////////////////////////////////////////////////////// |
| 125 // ImageCacheEntryMetadata | 120 // ImageCacheEntryMetadata |
| 126 // | 121 // |
| 127 // All metadata necessary to uniquely identify a cached image. | 122 // All metadata necessary to uniquely identify a cached image. |
| 128 struct ImageCacheEntryMetadata { | 123 struct ImageCacheEntryMetadata { |
| 129 ImageCacheEntryMetadata(int resource_id, | 124 ImageCacheEntryMetadata(int resource_id, |
| 130 SkColor fill_color, | 125 SkColor fill_color, |
| 131 SkColor stroke_color, | 126 SkColor stroke_color, |
| 132 bool use_fill_and_stroke_images, | 127 bool use_fill_and_stroke_images, |
| 133 float scale_factor, | 128 float scale_factor, |
| (...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 451 theme_r.height() - toolbar_overlap, false); | 446 theme_r.height() - toolbar_overlap, false); |
| 452 | 447 |
| 453 // Draw center. Instead of masking out the top portion we simply skip over it | 448 // Draw center. Instead of masking out the top portion we simply skip over it |
| 454 // by incrementing by the top padding, since it's a simple rectangle. | 449 // by incrementing by the top padding, since it's a simple rectangle. |
| 455 rect.Inset(g_mask_images.l_width, tab_insets.top(), g_mask_images.r_width, | 450 rect.Inset(g_mask_images.l_width, tab_insets.top(), g_mask_images.r_width, |
| 456 toolbar_overlap); | 451 toolbar_overlap); |
| 457 canvas->TileImageInt(fill_image, rect.x(), rect.y(), g_mask_images.l_width, | 452 canvas->TileImageInt(fill_image, rect.x(), rect.y(), g_mask_images.l_width, |
| 458 tab_insets.top(), rect.width(), rect.height()); | 453 tab_insets.top(), rect.width(), rect.height()); |
| 459 } | 454 } |
| 460 | 455 |
| 461 // Desaturates the favicon. Should only be used for when a tab encounters a | |
| 462 // network error state. | |
| 463 void PaintDesaturatedFavIcon(gfx::Canvas* canvas, | |
| 464 gfx::ImageSkia& favicon, | |
| 465 const gfx::Rect& bounds) { | |
| 466 color_utils::HSL shift = {kDesaturateHue, | |
| 467 kDesaturateSaturation, | |
| 468 kDesaturateLightness}; | |
| 469 gfx::ImageSkia desaturated_favicon = | |
| 470 gfx::ImageSkiaOperations::CreateHSLShiftedImage(favicon, shift); | |
| 471 if (!bounds.IsEmpty()) { | |
| 472 canvas->DrawImageInt(desaturated_favicon, 0, 0, | |
| 473 bounds.width(), bounds.height(), | |
| 474 bounds.x(), bounds.y(), bounds.width(), | |
| 475 bounds.height(), false); | |
| 476 } else { | |
| 477 canvas->DrawImageInt(desaturated_favicon, 0, 0); | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 } // namespace | 456 } // namespace |
| 482 | 457 |
| 483 //////////////////////////////////////////////////////////////////////////////// | 458 //////////////////////////////////////////////////////////////////////////////// |
| 484 // FaviconCrashAnimation | 459 // FaviconCrashAnimation |
| 485 // | 460 // |
| 486 // A custom animation subclass to manage the favicon crash animation. | 461 // A custom animation subclass to manage the favicon crash animation. |
| 487 class Tab::FaviconCrashAnimation : public gfx::LinearAnimation, | 462 class Tab::FaviconCrashAnimation : public gfx::LinearAnimation, |
| 488 public gfx::AnimationDelegate { | 463 public gfx::AnimationDelegate { |
| 489 public: | 464 public: |
| 490 explicit FaviconCrashAnimation(Tab* target) | 465 explicit FaviconCrashAnimation(Tab* target) |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 loading_start_time_ = base::TimeTicks(); | 627 loading_start_time_ = base::TimeTicks(); |
| 653 waiting_state_ = gfx::ThrobberWaitingState(); | 628 waiting_state_ = gfx::ThrobberWaitingState(); |
| 654 } | 629 } |
| 655 | 630 |
| 656 bool Tab::ThrobberView::CanProcessEventsWithinSubtree() const { | 631 bool Tab::ThrobberView::CanProcessEventsWithinSubtree() const { |
| 657 return false; | 632 return false; |
| 658 } | 633 } |
| 659 | 634 |
| 660 void Tab::ThrobberView::OnPaint(gfx::Canvas* canvas) { | 635 void Tab::ThrobberView::OnPaint(gfx::Canvas* canvas) { |
| 661 const TabRendererData::NetworkState state = owner_->data().network_state; | 636 const TabRendererData::NetworkState state = owner_->data().network_state; |
| 662 if (state == TabRendererData::NETWORK_STATE_NONE || | 637 if (state == TabRendererData::NETWORK_STATE_NONE) |
| 663 state == TabRendererData::NETWORK_STATE_ERROR) | |
| 664 return; | 638 return; |
| 665 | 639 |
| 666 const ui::ThemeProvider* tp = GetThemeProvider(); | 640 const ui::ThemeProvider* tp = GetThemeProvider(); |
| 667 const gfx::Rect bounds = GetLocalBounds(); | 641 const gfx::Rect bounds = GetLocalBounds(); |
| 668 if (state == TabRendererData::NETWORK_STATE_WAITING) { | 642 if (state == TabRendererData::NETWORK_STATE_WAITING) { |
| 669 if (waiting_start_time_ == base::TimeTicks()) | 643 if (waiting_start_time_ == base::TimeTicks()) |
| 670 waiting_start_time_ = base::TimeTicks::Now(); | 644 waiting_start_time_ = base::TimeTicks::Now(); |
| 671 | 645 |
| 672 waiting_state_.elapsed_time = base::TimeTicks::Now() - waiting_start_time_; | 646 waiting_state_.elapsed_time = base::TimeTicks::Now() - waiting_start_time_; |
| 673 gfx::PaintThrobberWaiting( | 647 gfx::PaintThrobberWaiting( |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 791 void Tab::AlertStateChanged() { | 765 void Tab::AlertStateChanged() { |
| 792 Layout(); | 766 Layout(); |
| 793 } | 767 } |
| 794 | 768 |
| 795 bool Tab::IsSelected() const { | 769 bool Tab::IsSelected() const { |
| 796 return controller_->IsTabSelected(this); | 770 return controller_->IsTabSelected(this); |
| 797 } | 771 } |
| 798 | 772 |
| 799 void Tab::SetData(const TabRendererData& data) { | 773 void Tab::SetData(const TabRendererData& data) { |
| 800 DCHECK(GetWidget()); | 774 DCHECK(GetWidget()); |
| 775 |
| 801 if (data_.Equals(data)) | 776 if (data_.Equals(data)) |
| 802 return; | 777 return; |
| 803 | 778 |
| 804 TabRendererData old(data_); | 779 TabRendererData old(data_); |
| 805 UpdateLoadingAnimation(data.network_state); | 780 UpdateLoadingAnimation(data.network_state); |
| 806 data_ = data; | 781 data_ = data; |
| 807 | 782 |
| 808 base::string16 title = data_.title; | 783 base::string16 title = data_.title; |
| 809 if (title.empty()) { | 784 if (title.empty()) { |
| 810 title = data_.loading ? | 785 title = data_.loading ? |
| (...skipping 21 matching lines...) Expand all Loading... |
| 832 showing_pinned_tab_title_changed_indicator_ = false; | 807 showing_pinned_tab_title_changed_indicator_ = false; |
| 833 | 808 |
| 834 DataChanged(old); | 809 DataChanged(old); |
| 835 | 810 |
| 836 Layout(); | 811 Layout(); |
| 837 SchedulePaint(); | 812 SchedulePaint(); |
| 838 } | 813 } |
| 839 | 814 |
| 840 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { | 815 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { |
| 841 if (state == data_.network_state && | 816 if (state == data_.network_state && |
| 842 (state == TabRendererData::NETWORK_STATE_NONE || | 817 state == TabRendererData::NETWORK_STATE_NONE) { |
| 843 state == TabRendererData::NETWORK_STATE_ERROR)) { | 818 // If the network state is none and hasn't changed, do nothing. Otherwise we |
| 844 // If the network state is none or is an network error and hasn't changed, | 819 // need to advance the animation frame. |
| 845 // do nothing. Otherwise we need to advance the animation frame. | |
| 846 return; | 820 return; |
| 847 } | 821 } |
| 848 | 822 |
| 849 data_.network_state = state; | 823 data_.network_state = state; |
| 850 AdvanceLoadingAnimation(); | 824 AdvanceLoadingAnimation(); |
| 851 } | 825 } |
| 852 | 826 |
| 853 void Tab::StartPulse() { | 827 void Tab::StartPulse() { |
| 854 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); | 828 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); |
| 855 } | 829 } |
| (...skipping 710 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1566 const gfx::Rect& favicon_draw_bounds) { | 1540 const gfx::Rect& favicon_draw_bounds) { |
| 1567 // The pinned tab title changed indicator consists of two parts: | 1541 // The pinned tab title changed indicator consists of two parts: |
| 1568 // . a clear (totally transparent) part over the bottom right (or left in rtl) | 1542 // . a clear (totally transparent) part over the bottom right (or left in rtl) |
| 1569 // of the favicon. This is done by drawing the favicon to a canvas, then | 1543 // of the favicon. This is done by drawing the favicon to a canvas, then |
| 1570 // drawing the clear part on top of the favicon. | 1544 // drawing the clear part on top of the favicon. |
| 1571 // . a circle in the bottom right (or left in rtl) of the favicon. | 1545 // . a circle in the bottom right (or left in rtl) of the favicon. |
| 1572 if (!favicon_.isNull()) { | 1546 if (!favicon_.isNull()) { |
| 1573 const float kIndicatorCropRadius = 4.5; | 1547 const float kIndicatorCropRadius = 4.5; |
| 1574 gfx::Canvas icon_canvas(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize), | 1548 gfx::Canvas icon_canvas(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize), |
| 1575 canvas->image_scale(), false); | 1549 canvas->image_scale(), false); |
| 1576 | 1550 icon_canvas.DrawImageInt(favicon_, 0, 0); |
| 1577 if (data().network_state == TabRendererData::NETWORK_STATE_ERROR) | |
| 1578 PaintDesaturatedFavIcon(&icon_canvas, favicon_, gfx::Rect()); | |
| 1579 else | |
| 1580 icon_canvas.DrawImageInt(favicon_, 0, 0); | |
| 1581 | |
| 1582 SkPaint clear_paint; | 1551 SkPaint clear_paint; |
| 1583 clear_paint.setAntiAlias(true); | 1552 clear_paint.setAntiAlias(true); |
| 1584 clear_paint.setXfermodeMode(SkXfermode::kClear_Mode); | 1553 clear_paint.setXfermodeMode(SkXfermode::kClear_Mode); |
| 1585 const int circle_x = base::i18n::IsRTL() ? 0 : gfx::kFaviconSize; | 1554 const int circle_x = base::i18n::IsRTL() ? 0 : gfx::kFaviconSize; |
| 1586 icon_canvas.DrawCircle(gfx::PointF(circle_x, gfx::kFaviconSize), | 1555 icon_canvas.DrawCircle(gfx::PointF(circle_x, gfx::kFaviconSize), |
| 1587 kIndicatorCropRadius, clear_paint); | 1556 kIndicatorCropRadius, clear_paint); |
| 1588 canvas->DrawImageInt(gfx::ImageSkia(icon_canvas.ExtractImageRep()), 0, 0, | 1557 canvas->DrawImageInt(gfx::ImageSkia(icon_canvas.ExtractImageRep()), 0, 0, |
| 1589 favicon_draw_bounds.width(), | 1558 favicon_draw_bounds.width(), |
| 1590 favicon_draw_bounds.height(), favicon_draw_bounds.x(), | 1559 favicon_draw_bounds.height(), favicon_draw_bounds.x(), |
| 1591 favicon_draw_bounds.y(), favicon_draw_bounds.width(), | 1560 favicon_draw_bounds.y(), favicon_draw_bounds.width(), |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1608 | 1577 |
| 1609 void Tab::PaintIcon(gfx::Canvas* canvas) { | 1578 void Tab::PaintIcon(gfx::Canvas* canvas) { |
| 1610 gfx::Rect bounds = favicon_bounds_; | 1579 gfx::Rect bounds = favicon_bounds_; |
| 1611 bounds.set_x(GetMirroredXForRect(bounds)); | 1580 bounds.set_x(GetMirroredXForRect(bounds)); |
| 1612 bounds.Offset(0, favicon_hiding_offset_); | 1581 bounds.Offset(0, favicon_hiding_offset_); |
| 1613 bounds.Intersect(GetContentsBounds()); | 1582 bounds.Intersect(GetContentsBounds()); |
| 1614 if (bounds.IsEmpty()) | 1583 if (bounds.IsEmpty()) |
| 1615 return; | 1584 return; |
| 1616 | 1585 |
| 1617 // Throbber will do its own painting. | 1586 // Throbber will do its own painting. |
| 1618 if (data().network_state != TabRendererData::NETWORK_STATE_NONE && | 1587 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) |
| 1619 data().network_state != TabRendererData::NETWORK_STATE_ERROR) | |
| 1620 return; | 1588 return; |
| 1621 | 1589 |
| 1622 // Ensure that |favicon_| is created. | 1590 // Ensure that |favicon_| is created. |
| 1623 if (favicon_.isNull()) { | 1591 if (favicon_.isNull()) { |
| 1624 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); | 1592 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); |
| 1625 favicon_ = should_display_crashed_favicon_ | 1593 favicon_ = should_display_crashed_favicon_ |
| 1626 ? *rb->GetImageSkiaNamed(IDR_CRASH_SAD_FAVICON) | 1594 ? *rb->GetImageSkiaNamed(IDR_CRASH_SAD_FAVICON) |
| 1627 : data().favicon; | 1595 : data().favicon; |
| 1628 // Themify the icon if it's a chrome:// page or if it's the sadtab favicon. | 1596 // Themify the icon if it's a chrome:// page or if it's the sadtab favicon. |
| 1629 // This ensures chrome:// pages are visible over the tab background. This is | 1597 // This ensures chrome:// pages are visible over the tab background. This is |
| 1630 // similar to code in the bookmarks bar. | 1598 // similar to code in the bookmarks bar. |
| 1631 if (!favicon_.isNull() && | 1599 if (!favicon_.isNull() && |
| 1632 (should_display_crashed_favicon_ || | 1600 (should_display_crashed_favicon_ || |
| 1633 favicon_.BackedBySameObjectAs( | 1601 favicon_.BackedBySameObjectAs( |
| 1634 *rb->GetImageSkiaNamed(IDR_DEFAULT_FAVICON)) || | 1602 *rb->GetImageSkiaNamed(IDR_DEFAULT_FAVICON)) || |
| 1635 ShouldThemifyFaviconForUrl(data().url))) { | 1603 ShouldThemifyFaviconForUrl(data().url))) { |
| 1636 favicon_ = gfx::ImageSkiaOperations::CreateHSLShiftedImage( | 1604 favicon_ = gfx::ImageSkiaOperations::CreateHSLShiftedImage( |
| 1637 favicon_, GetThemeProvider()->GetTint(ThemeProperties::TINT_BUTTONS)); | 1605 favicon_, GetThemeProvider()->GetTint(ThemeProperties::TINT_BUTTONS)); |
| 1638 } | 1606 } |
| 1639 } | 1607 } |
| 1640 | 1608 |
| 1641 if (showing_pinned_tab_title_changed_indicator_ && | 1609 if (showing_pinned_tab_title_changed_indicator_ && |
| 1642 !should_display_crashed_favicon_) { | 1610 !should_display_crashed_favicon_) { |
| 1643 PaintPinnedTabTitleChangedIndicatorAndIcon(canvas, bounds); | 1611 PaintPinnedTabTitleChangedIndicatorAndIcon(canvas, bounds); |
| 1644 } else if (!favicon_.isNull()) { | 1612 } else if (!favicon_.isNull()) { |
| 1645 // Desaturate favicons of tabs with network errors. | 1613 canvas->DrawImageInt(favicon_, 0, 0, bounds.width(), bounds.height(), |
| 1646 if (data().network_state == TabRendererData::NETWORK_STATE_ERROR) { | 1614 bounds.x(), bounds.y(), bounds.width(), |
| 1647 PaintDesaturatedFavIcon(canvas, favicon_, bounds); | 1615 bounds.height(), false); |
| 1648 } else { | |
| 1649 canvas->DrawImageInt(favicon_, 0, 0, bounds.width(), bounds.height(), | |
| 1650 bounds.x(), bounds.y(), bounds.width(), | |
| 1651 bounds.height(), false); | |
| 1652 } | |
| 1653 } | 1616 } |
| 1654 } | 1617 } |
| 1655 | 1618 |
| 1656 void Tab::AdvanceLoadingAnimation() { | 1619 void Tab::AdvanceLoadingAnimation() { |
| 1657 const TabRendererData::NetworkState state = data().network_state; | 1620 const TabRendererData::NetworkState state = data().network_state; |
| 1658 if (controller_->IsImmersiveStyle()) { | 1621 if (controller_->IsImmersiveStyle()) { |
| 1659 throbber_->SetVisible(false); | 1622 throbber_->SetVisible(false); |
| 1660 if (state == TabRendererData::NETWORK_STATE_WAITING) { | 1623 if (state == TabRendererData::NETWORK_STATE_WAITING) { |
| 1661 // Waiting steps backwards. | 1624 // Waiting steps backwards. |
| 1662 immersive_loading_step_ = | 1625 immersive_loading_step_ = |
| 1663 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % | 1626 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % |
| 1664 kImmersiveLoadingStepCount; | 1627 kImmersiveLoadingStepCount; |
| 1665 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { | 1628 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { |
| 1666 immersive_loading_step_ = | 1629 immersive_loading_step_ = |
| 1667 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount; | 1630 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount; |
| 1668 } else { | 1631 } else { |
| 1669 immersive_loading_step_ = 0; | 1632 immersive_loading_step_ = 0; |
| 1670 } | 1633 } |
| 1671 | 1634 |
| 1672 SchedulePaintInRect(GetImmersiveBarRect()); | 1635 SchedulePaintInRect(GetImmersiveBarRect()); |
| 1673 return; | 1636 return; |
| 1674 } | 1637 } |
| 1675 | 1638 |
| 1676 if (state == TabRendererData::NETWORK_STATE_NONE || | 1639 if (state == TabRendererData::NETWORK_STATE_NONE) { |
| 1677 state == TabRendererData::NETWORK_STATE_ERROR) { | |
| 1678 throbber_->ResetStartTimes(); | 1640 throbber_->ResetStartTimes(); |
| 1679 throbber_->SetVisible(false); | 1641 throbber_->SetVisible(false); |
| 1680 ScheduleIconPaint(); | 1642 ScheduleIconPaint(); |
| 1681 return; | 1643 return; |
| 1682 } | 1644 } |
| 1683 | 1645 |
| 1684 // Since the throbber can animate for a long time, paint to a separate layer | 1646 // Since the throbber can animate for a long time, paint to a separate layer |
| 1685 // when possible to reduce repaint overhead. | 1647 // when possible to reduce repaint overhead. |
| 1686 const bool paint_to_layer = controller_->CanPaintThrobberToLayer(); | 1648 const bool paint_to_layer = controller_->CanPaintThrobberToLayer(); |
| 1687 if (paint_to_layer != !!throbber_->layer()) { | 1649 if (paint_to_layer != !!throbber_->layer()) { |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1812 gfx::Rect Tab::GetImmersiveBarRect() const { | 1774 gfx::Rect Tab::GetImmersiveBarRect() const { |
| 1813 // The main bar is as wide as the normal tab's horizontal top line. | 1775 // The main bar is as wide as the normal tab's horizontal top line. |
| 1814 // This top line of the tab extends a few pixels left and right of the | 1776 // This top line of the tab extends a few pixels left and right of the |
| 1815 // center image due to pixels in the rounded corner images. | 1777 // center image due to pixels in the rounded corner images. |
| 1816 const int kBarPadding = 1; | 1778 const int kBarPadding = 1; |
| 1817 int main_bar_left = g_active_images.l_width - kBarPadding; | 1779 int main_bar_left = g_active_images.l_width - kBarPadding; |
| 1818 int main_bar_right = width() - g_active_images.r_width + kBarPadding; | 1780 int main_bar_right = width() - g_active_images.r_width + kBarPadding; |
| 1819 return gfx::Rect( | 1781 return gfx::Rect( |
| 1820 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); | 1782 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); |
| 1821 } | 1783 } |
| OLD | NEW |