| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 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/views/status_bubble_views.h" | 5 #include "chrome/browser/views/status_bubble_views.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "app/animation.h" | 9 #include "app/animation.h" |
| 10 #include "app/l10n_util.h" | 10 #include "app/l10n_util.h" |
| 11 #include "app/resource_bundle.h" | 11 #include "app/resource_bundle.h" |
| 12 #include "app/text_elider.h" | 12 #include "app/text_elider.h" |
| 13 #include "base/i18n/rtl.h" | 13 #include "base/i18n/rtl.h" |
| 14 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
| 15 #include "base/string_util.h" | 15 #include "base/string_util.h" |
| 16 #include "chrome/browser/browser_theme_provider.h" | 16 #include "chrome/browser/browser_theme_provider.h" |
| 17 #include "gfx/canvas.h" | 17 #include "gfx/canvas.h" |
| 18 #include "gfx/point.h" | 18 #include "gfx/point.h" |
| 19 #include "googleurl/src/gurl.h" | 19 #include "googleurl/src/gurl.h" |
| 20 #include "grit/generated_resources.h" | 20 #include "grit/generated_resources.h" |
| 21 #include "grit/theme_resources.h" | 21 #include "grit/theme_resources.h" |
| 22 #include "net/base/net_util.h" | 22 #include "net/base/net_util.h" |
| 23 #include "third_party/skia/include/core/SkPaint.h" | 23 #include "third_party/skia/include/core/SkPaint.h" |
| 24 #include "third_party/skia/include/core/SkPath.h" | 24 #include "third_party/skia/include/core/SkPath.h" |
| 25 #include "third_party/skia/include/core/SkRect.h" | 25 #include "third_party/skia/include/core/SkRect.h" |
| 26 #include "views/controls/label.h" | 26 #include "views/controls/label.h" |
| 27 #include "views/controls/scrollbar/native_scroll_bar.h" |
| 27 #include "views/screen.h" | 28 #include "views/screen.h" |
| 28 #include "views/widget/root_view.h" | 29 #include "views/widget/root_view.h" |
| 29 #include "views/widget/widget.h" | 30 #include "views/widget/widget.h" |
| 30 #include "views/window/window.h" | 31 #include "views/window/window.h" |
| 31 | 32 |
| 32 using views::Widget; | 33 using views::Widget; |
| 33 | 34 |
| 34 // The alpha and color of the bubble's shadow. | 35 // The alpha and color of the bubble's shadow. |
| 35 static const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0); | 36 static const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0); |
| 36 | 37 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 52 // Delays before we start hiding or showing the bubble after we receive a | 53 // Delays before we start hiding or showing the bubble after we receive a |
| 53 // show or hide request. | 54 // show or hide request. |
| 54 static const int kShowDelay = 80; | 55 static const int kShowDelay = 80; |
| 55 static const int kHideDelay = 250; | 56 static const int kHideDelay = 250; |
| 56 | 57 |
| 57 // How long each fade should last for. | 58 // How long each fade should last for. |
| 58 static const int kShowFadeDurationMS = 120; | 59 static const int kShowFadeDurationMS = 120; |
| 59 static const int kHideFadeDurationMS = 200; | 60 static const int kHideFadeDurationMS = 200; |
| 60 static const int kFramerate = 25; | 61 static const int kFramerate = 25; |
| 61 | 62 |
| 63 // How long each expansion step should take. |
| 64 static const int kMinExpansionStepDurationMS = 20; |
| 65 static const int kMaxExpansionStepDurationMS = 150; |
| 66 |
| 62 // View ----------------------------------------------------------------------- | 67 // View ----------------------------------------------------------------------- |
| 63 // StatusView manages the display of the bubble, applying text changes and | 68 // StatusView manages the display of the bubble, applying text changes and |
| 64 // fading in or out the bubble as required. | 69 // fading in or out the bubble as required. |
| 65 class StatusBubbleViews::StatusView : public views::Label, | 70 class StatusBubbleViews::StatusView : public views::Label, |
| 66 public Animation, | 71 public Animation, |
| 67 public AnimationDelegate { | 72 public AnimationDelegate { |
| 68 public: | 73 public: |
| 69 StatusView(StatusBubble* status_bubble, views::Widget* popup, | 74 StatusView(StatusBubble* status_bubble, views::Widget* popup, |
| 70 ThemeProvider* theme_provider) | 75 ThemeProvider* theme_provider) |
| 71 : ALLOW_THIS_IN_INITIALIZER_LIST(Animation(kFramerate, this)), | 76 : ALLOW_THIS_IN_INITIALIZER_LIST(Animation(kFramerate, this)), |
| (...skipping 26 matching lines...) Expand all Loading... |
| 98 }; | 103 }; |
| 99 | 104 |
| 100 enum BubbleStyle { | 105 enum BubbleStyle { |
| 101 STYLE_BOTTOM, | 106 STYLE_BOTTOM, |
| 102 STYLE_FLOATING, | 107 STYLE_FLOATING, |
| 103 STYLE_STANDARD, | 108 STYLE_STANDARD, |
| 104 STYLE_STANDARD_RIGHT | 109 STYLE_STANDARD_RIGHT |
| 105 }; | 110 }; |
| 106 | 111 |
| 107 // Set the bubble text to a certain value, hides the bubble if text is | 112 // Set the bubble text to a certain value, hides the bubble if text is |
| 108 // an empty string. | 113 // an empty string. Do not trigger animation sequence. |
| 109 void SetText(const std::wstring& text); | 114 void SetText(const std::wstring& text); |
| 110 | 115 |
| 116 // Set the bubble text to a certain value, or hide the bubble if text is |
| 117 // an empty string. Start animation sequence. |
| 118 void SetTextAndAnimate(const std::wstring& text); |
| 119 |
| 111 BubbleStage GetState() const { return stage_; } | 120 BubbleStage GetState() const { return stage_; } |
| 112 | 121 |
| 113 void SetStyle(BubbleStyle style); | 122 void SetStyle(BubbleStyle style); |
| 114 | 123 |
| 115 BubbleStyle GetStyle() const { return style_; } | 124 BubbleStyle GetStyle() const { return style_; } |
| 116 | 125 |
| 117 // Show the bubble instantly. | 126 // Show the bubble instantly. |
| 118 void Show(); | 127 void Show(); |
| 119 | 128 |
| 120 // Hide the bubble instantly. | 129 // Hide the bubble instantly. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 // fade-in can easily turn into a fade out, opacity_start_ is sometimes | 173 // fade-in can easily turn into a fade out, opacity_start_ is sometimes |
| 165 // a value between 0 and 1. | 174 // a value between 0 and 1. |
| 166 double opacity_start_; | 175 double opacity_start_; |
| 167 double opacity_end_; | 176 double opacity_end_; |
| 168 | 177 |
| 169 // Holds the theme provider of the frame that created us. | 178 // Holds the theme provider of the frame that created us. |
| 170 ThemeProvider* theme_provider_; | 179 ThemeProvider* theme_provider_; |
| 171 }; | 180 }; |
| 172 | 181 |
| 173 void StatusBubbleViews::StatusView::SetText(const std::wstring& text) { | 182 void StatusBubbleViews::StatusView::SetText(const std::wstring& text) { |
| 183 text_ = text; |
| 184 SchedulePaint(); |
| 185 } |
| 186 |
| 187 void StatusBubbleViews::StatusView::SetTextAndAnimate( |
| 188 const std::wstring& text) { |
| 174 if (text.empty()) { | 189 if (text.empty()) { |
| 175 // The string was empty. | 190 // The string was empty. |
| 176 StartHiding(); | 191 StartHiding(); |
| 177 } else { | 192 } else { |
| 178 // We want to show the string. | 193 // We want to show the string. |
| 179 text_ = text; | 194 text_ = text; |
| 180 StartShowing(); | 195 StartShowing(); |
| 181 } | 196 } |
| 182 | 197 |
| 183 SchedulePaint(); | 198 SchedulePaint(); |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 439 (SkColorGetB(text_color) + SkColorGetR(toolbar_color)) / 2); | 454 (SkColorGetB(text_color) + SkColorGetR(toolbar_color)) / 2); |
| 440 canvas->DrawStringInt(text_, | 455 canvas->DrawStringInt(text_, |
| 441 views::Label::font(), | 456 views::Label::font(), |
| 442 text_color, | 457 text_color, |
| 443 body_bounds.x(), | 458 body_bounds.x(), |
| 444 body_bounds.y(), | 459 body_bounds.y(), |
| 445 body_bounds.width(), | 460 body_bounds.width(), |
| 446 body_bounds.height()); | 461 body_bounds.height()); |
| 447 } | 462 } |
| 448 | 463 |
| 464 // StatusViewExpander --------------------------------------------------------- |
| 465 // Manages the expansion and contraction of the status bubble as it accommodates |
| 466 // URLs too long to fit in the standard bubble. Changes are passed through the |
| 467 // StatusView to paint. |
| 468 class StatusBubbleViews::StatusViewExpander : public Animation, |
| 469 public AnimationDelegate { |
| 470 public: |
| 471 StatusViewExpander(StatusBubble* status_bubble, |
| 472 StatusView* status_view) |
| 473 : ALLOW_THIS_IN_INITIALIZER_LIST(Animation(kFramerate, this)), |
| 474 status_bubble_(status_bubble), |
| 475 status_view_(status_view), |
| 476 expansion_start_(0), |
| 477 expansion_end_(0) { |
| 478 } |
| 479 |
| 480 // Manage the expansion of the bubble. |
| 481 void StartExpansion(std::wstring expanded_text, int current_width, |
| 482 int expansion_end); |
| 483 |
| 484 // Set width of fully expanded bubble. |
| 485 void SetExpandedWidth(int expanded_width); |
| 486 |
| 487 private: |
| 488 // Animation functions. |
| 489 int GetCurrentBubbleWidth(); |
| 490 void SetBubbleWidth(int width); |
| 491 void AnimateToState(double state); |
| 492 void AnimationEnded(const Animation* animation); |
| 493 |
| 494 // Manager that owns us. |
| 495 StatusBubble* status_bubble_; |
| 496 |
| 497 // Change the bounds and text of this view. |
| 498 StatusView* status_view_; |
| 499 |
| 500 // Text elided (if needed) to fit maximum status bar width. |
| 501 std::wstring expanded_text_; |
| 502 |
| 503 // Widths at expansion start and end. |
| 504 int expansion_start_; |
| 505 int expansion_end_; |
| 506 }; |
| 507 |
| 508 void StatusBubbleViews::StatusViewExpander::AnimateToState(double state) { |
| 509 SetBubbleWidth(GetCurrentBubbleWidth()); |
| 510 } |
| 511 |
| 512 void StatusBubbleViews::StatusViewExpander::AnimationEnded( |
| 513 const Animation* animation) { |
| 514 SetBubbleWidth(expansion_end_); |
| 515 status_view_->SetText(expanded_text_); |
| 516 } |
| 517 |
| 518 void StatusBubbleViews::StatusViewExpander::StartExpansion( |
| 519 std::wstring expanded_text, int expansion_start, |
| 520 int expansion_end) { |
| 521 expanded_text_ = expanded_text; |
| 522 expansion_start_ = expansion_start; |
| 523 expansion_end_ = expansion_end; |
| 524 int min_duration = std::max(kMinExpansionStepDurationMS, |
| 525 static_cast<int>(kMaxExpansionStepDurationMS * |
| 526 (expansion_end - expansion_start) / 100.0)); |
| 527 SetDuration(std::min(kMaxExpansionStepDurationMS, min_duration)); |
| 528 Start(); |
| 529 } |
| 530 |
| 531 int StatusBubbleViews::StatusViewExpander::GetCurrentBubbleWidth() { |
| 532 return static_cast<int>(expansion_start_ + |
| 533 (expansion_end_ - expansion_start_) * Animation::GetCurrentValue()); |
| 534 } |
| 535 |
| 536 void StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) { |
| 537 status_bubble_->SetBubbleWidth(width); |
| 538 status_view_->SchedulePaint(); |
| 539 } |
| 540 |
| 449 // StatusBubble --------------------------------------------------------------- | 541 // StatusBubble --------------------------------------------------------------- |
| 450 | 542 |
| 451 const int StatusBubbleViews::kShadowThickness = 1; | 543 const int StatusBubbleViews::kShadowThickness = 1; |
| 452 | 544 |
| 453 StatusBubbleViews::StatusBubbleViews(views::Widget* frame) | 545 StatusBubbleViews::StatusBubbleViews(views::Widget* frame) |
| 454 : offset_(0), | 546 : offset_(0), |
| 455 popup_(NULL), | 547 popup_(NULL), |
| 456 opacity_(0), | 548 opacity_(0), |
| 457 frame_(frame), | 549 frame_(frame), |
| 458 view_(NULL), | 550 view_(NULL), |
| 459 download_shelf_is_visible_(false) { | 551 download_shelf_is_visible_(false), |
| 552 is_expanded_(false), |
| 553 ALLOW_THIS_IN_INITIALIZER_LIST(expand_timer_factory_(this)) { |
| 554 expand_view_.reset(); |
| 460 } | 555 } |
| 461 | 556 |
| 462 StatusBubbleViews::~StatusBubbleViews() { | 557 StatusBubbleViews::~StatusBubbleViews() { |
| 558 CancelExpandTimer(); |
| 463 if (popup_.get()) | 559 if (popup_.get()) |
| 464 popup_->CloseNow(); | 560 popup_->CloseNow(); |
| 465 } | 561 } |
| 466 | 562 |
| 467 void StatusBubbleViews::Init() { | 563 void StatusBubbleViews::Init() { |
| 468 if (!popup_.get()) { | 564 if (!popup_.get()) { |
| 469 popup_.reset(Widget::CreatePopupWidget(Widget::Transparent, | 565 popup_.reset(Widget::CreatePopupWidget(Widget::Transparent, |
| 470 Widget::NotAcceptEvents, | 566 Widget::NotAcceptEvents, |
| 471 Widget::NotDeleteOnDestroy)); | 567 Widget::NotDeleteOnDestroy)); |
| 472 if (!view_) | 568 if (!view_) |
| 473 view_ = new StatusView(this, popup_.get(), frame_->GetThemeProvider()); | 569 view_ = new StatusView(this, popup_.get(), frame_->GetThemeProvider()); |
| 570 if (!expand_view_.get()) |
| 571 expand_view_.reset(new StatusViewExpander(this, view_)); |
| 474 popup_->SetOpacity(0x00); | 572 popup_->SetOpacity(0x00); |
| 475 popup_->Init(frame_->GetNativeView(), gfx::Rect()); | 573 popup_->Init(frame_->GetNativeView(), gfx::Rect()); |
| 476 popup_->SetContentsView(view_); | 574 popup_->SetContentsView(view_); |
| 477 Reposition(); | 575 Reposition(); |
| 478 popup_->Show(); | 576 popup_->Show(); |
| 479 } | 577 } |
| 480 } | 578 } |
| 481 | 579 |
| 482 void StatusBubbleViews::Reposition() { | 580 void StatusBubbleViews::Reposition() { |
| 483 if (popup_.get()) { | 581 if (popup_.get()) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 | 615 |
| 518 if (status_text_ == status_text) | 616 if (status_text_ == status_text) |
| 519 return; | 617 return; |
| 520 | 618 |
| 521 if (!IsFrameVisible()) | 619 if (!IsFrameVisible()) |
| 522 return; // Don't show anything if the parent isn't visible. | 620 return; // Don't show anything if the parent isn't visible. |
| 523 | 621 |
| 524 Init(); | 622 Init(); |
| 525 status_text_ = status_text; | 623 status_text_ = status_text; |
| 526 if (!status_text_.empty()) { | 624 if (!status_text_.empty()) { |
| 527 view_->SetText(status_text); | 625 view_->SetTextAndAnimate(status_text); |
| 528 view_->Show(); | 626 view_->Show(); |
| 529 } else if (!url_text_.empty()) { | 627 } else if (!url_text_.empty()) { |
| 530 view_->SetText(url_text_); | 628 view_->SetTextAndAnimate(url_text_); |
| 531 } else { | 629 } else { |
| 532 view_->SetText(std::wstring()); | 630 view_->SetTextAndAnimate(std::wstring()); |
| 533 } | 631 } |
| 534 } | 632 } |
| 535 | 633 |
| 536 void StatusBubbleViews::SetURL(const GURL& url, const std::wstring& languages) { | 634 void StatusBubbleViews::SetURL(const GURL& url, const std::wstring& languages) { |
| 635 languages_ = languages; |
| 636 url_ = url; |
| 537 if (size_.IsEmpty()) | 637 if (size_.IsEmpty()) |
| 538 return; // We have no bounds, don't attempt to show the popup. | 638 return; // We have no bounds, don't attempt to show the popup. |
| 539 | 639 |
| 540 Init(); | 640 Init(); |
| 541 | 641 |
| 542 // If we want to clear a displayed URL but there is a status still to | 642 // If we want to clear a displayed URL but there is a status still to |
| 543 // display, display that status instead. | 643 // display, display that status instead. |
| 544 if (url.is_empty() && !status_text_.empty()) { | 644 if (url.is_empty() && !status_text_.empty()) { |
| 545 url_text_ = std::wstring(); | 645 url_text_ = std::wstring(); |
| 546 if (IsFrameVisible()) | 646 if (IsFrameVisible()) |
| 547 view_->SetText(status_text_); | 647 view_->SetTextAndAnimate(status_text_); |
| 548 return; | 648 return; |
| 549 } | 649 } |
| 550 | 650 |
| 651 // Reset expansion state only when bubble is completely hidden. |
| 652 if (view_->GetState() == StatusView::BUBBLE_HIDDEN) { |
| 653 is_expanded_ = false; |
| 654 SetBubbleWidth(GetStandardStatusBubbleWidth()); |
| 655 } |
| 656 |
| 551 // Set Elided Text corresponding to the GURL object. | 657 // Set Elided Text corresponding to the GURL object. |
| 552 gfx::Rect popup_bounds; | 658 gfx::Rect popup_bounds; |
| 553 popup_->GetBounds(&popup_bounds, true); | 659 popup_->GetBounds(&popup_bounds, true); |
| 554 int text_width = static_cast<int>(popup_bounds.width() - | 660 int text_width = static_cast<int>(popup_bounds.width() - |
| 555 (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1); | 661 (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1); |
| 556 url_text_ = gfx::ElideUrl(url, view_->Label::font(), text_width, | 662 url_text_ = gfx::ElideUrl(url, view_->Label::font(), text_width, |
| 557 languages); | 663 languages); |
| 664 |
| 665 std::wstring original_url_text = net::FormatUrl(url, languages); |
| 558 | 666 |
| 559 // An URL is always treated as a left-to-right string. On right-to-left UIs | 667 // An URL is always treated as a left-to-right string. On right-to-left UIs |
| 560 // we need to explicitly mark the URL as LTR to make sure it is displayed | 668 // we need to explicitly mark the URL as LTR to make sure it is displayed |
| 561 // correctly. | 669 // correctly. |
| 562 if (base::i18n::IsRTL() && !url_text_.empty()) | 670 if (base::i18n::IsRTL() && !url_text_.empty()) |
| 563 base::i18n::WrapStringWithLTRFormatting(&url_text_); | 671 base::i18n::WrapStringWithLTRFormatting(&url_text_); |
| 564 | 672 |
| 565 if (IsFrameVisible()) | 673 if (IsFrameVisible()) { |
| 566 view_->SetText(url_text_); | 674 view_->SetTextAndAnimate(url_text_); |
| 675 |
| 676 CancelExpandTimer(); |
| 677 |
| 678 // If bubble is already in expanded state, shift to adjust to new text |
| 679 // size (shrinking or expanding). Otherwise delay. |
| 680 if (is_expanded_ && !url.is_empty()) |
| 681 ExpandBubble(); |
| 682 else if (original_url_text.length() > url_text_.length()) |
| 683 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 684 expand_timer_factory_.NewRunnableMethod( |
| 685 &StatusBubbleViews::ExpandBubble), kExpandHoverDelay); |
| 686 } |
| 567 } | 687 } |
| 568 | 688 |
| 569 void StatusBubbleViews::Hide() { | 689 void StatusBubbleViews::Hide() { |
| 570 status_text_ = std::wstring(); | 690 status_text_ = std::wstring(); |
| 571 url_text_ = std::wstring(); | 691 url_text_ = std::wstring(); |
| 572 if (view_) | 692 if (view_) |
| 573 view_->Hide(); | 693 view_->Hide(); |
| 574 } | 694 } |
| 575 | 695 |
| 576 void StatusBubbleViews::MouseMoved(const gfx::Point& location, | 696 void StatusBubbleViews::MouseMoved(const gfx::Point& location, |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 679 } | 799 } |
| 680 } | 800 } |
| 681 | 801 |
| 682 bool StatusBubbleViews::IsFrameVisible() { | 802 bool StatusBubbleViews::IsFrameVisible() { |
| 683 if (!frame_->IsVisible()) | 803 if (!frame_->IsVisible()) |
| 684 return false; | 804 return false; |
| 685 | 805 |
| 686 views::Window* window = frame_->GetWindow(); | 806 views::Window* window = frame_->GetWindow(); |
| 687 return !window || !window->IsMinimized(); | 807 return !window || !window->IsMinimized(); |
| 688 } | 808 } |
| 809 |
| 810 void StatusBubbleViews::ExpandBubble() { |
| 811 // Elide URL to maximum possible size, then check actual length (it may |
| 812 // still be too long to fit) before expanding bubble. |
| 813 gfx::Rect popup_bounds; |
| 814 popup_->GetBounds(&popup_bounds, true); |
| 815 int max_status_bubble_width = GetMaxStatusBubbleWidth(); |
| 816 url_text_ = gfx::ElideUrl(url_, view_->Label::font(), |
| 817 max_status_bubble_width, languages_); |
| 818 int expanded_bubble_width = |
| 819 std::max(GetStandardStatusBubbleWidth(), |
| 820 std::min(view_->Label::font().GetStringWidth(url_text_) + |
| 821 (kShadowThickness * 2) + kTextPositionX + |
| 822 kTextHorizPadding + 1, |
| 823 max_status_bubble_width)); |
| 824 is_expanded_ = true; |
| 825 expand_view_->StartExpansion(url_text_, popup_bounds.width(), |
| 826 expanded_bubble_width); |
| 827 } |
| 828 |
| 829 int StatusBubbleViews::GetStandardStatusBubbleWidth() { |
| 830 gfx::Rect frame_bounds; |
| 831 frame_->GetBounds(&frame_bounds, false); |
| 832 return frame_bounds.width() / 3; |
| 833 } |
| 834 |
| 835 int StatusBubbleViews::GetMaxStatusBubbleWidth() { |
| 836 gfx::Rect frame_bounds; |
| 837 frame_->GetBounds(&frame_bounds, false); |
| 838 return static_cast<int>(frame_bounds.width() - (kShadowThickness * 2) - |
| 839 kTextPositionX - kTextHorizPadding - 1 - |
| 840 views::NativeScrollBar::GetVerticalScrollBarWidth()); |
| 841 } |
| 842 |
| 843 void StatusBubbleViews::SetBubbleWidth(int width) { |
| 844 size_.set_width(width); |
| 845 SetBounds(position_.x(), position_.y(), size_.width(), size_.height()); |
| 846 } |
| 847 |
| 848 void StatusBubbleViews::CancelExpandTimer() { |
| 849 if (!expand_timer_factory_.empty()) |
| 850 expand_timer_factory_.RevokeAll(); |
| 851 } |
| OLD | NEW |