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 //////////////////////////////////////////////////////////////////////////////// | |
414 // ImageCacheEntry | 345 // ImageCacheEntry |
415 | 346 |
416 Tab::ImageCacheEntry::ImageCacheEntry() | 347 Tab::ImageCacheEntry::ImageCacheEntry() |
417 : resource_id(-1), | 348 : resource_id(-1), |
418 scale_factor(ui::SCALE_FACTOR_NONE) { | 349 scale_factor(ui::SCALE_FACTOR_NONE) { |
419 } | 350 } |
420 | 351 |
421 Tab::ImageCacheEntry::~ImageCacheEntry() {} | 352 Tab::ImageCacheEntry::~ImageCacheEntry() {} |
422 | 353 |
423 //////////////////////////////////////////////////////////////////////////////// | 354 //////////////////////////////////////////////////////////////////////////////// |
(...skipping 10 matching lines...) Expand all Loading... |
434 // Tab, public: | 365 // Tab, public: |
435 | 366 |
436 Tab::Tab(TabController* controller) | 367 Tab::Tab(TabController* controller) |
437 : controller_(controller), | 368 : controller_(controller), |
438 closing_(false), | 369 closing_(false), |
439 dragging_(false), | 370 dragging_(false), |
440 detached_(false), | 371 detached_(false), |
441 favicon_hiding_offset_(0), | 372 favicon_hiding_offset_(0), |
442 immersive_loading_step_(0), | 373 immersive_loading_step_(0), |
443 should_display_crashed_favicon_(false), | 374 should_display_crashed_favicon_(false), |
444 throbber_(nullptr), | |
445 media_indicator_button_(nullptr), | 375 media_indicator_button_(nullptr), |
446 close_button_(nullptr), | 376 close_button_(nullptr), |
447 title_(new views::Label()), | 377 title_(new views::Label()), |
448 tab_activated_with_last_tap_down_(false), | 378 tab_activated_with_last_tap_down_(false), |
449 hover_controller_(this), | 379 hover_controller_(this), |
450 showing_icon_(false), | 380 showing_icon_(false), |
451 showing_media_indicator_(false), | 381 showing_media_indicator_(false), |
452 showing_close_button_(false), | 382 showing_close_button_(false), |
453 button_color_(SK_ColorTRANSPARENT) { | 383 button_color_(SK_ColorTRANSPARENT) { |
454 DCHECK(controller); | 384 DCHECK(controller); |
455 InitTabResources(); | 385 InitTabResources(); |
456 | 386 |
457 // So we get don't get enter/exit on children and don't prematurely stop the | 387 // So we get don't get enter/exit on children and don't prematurely stop the |
458 // hover. | 388 // hover. |
459 set_notify_enter_exit_on_child(true); | 389 set_notify_enter_exit_on_child(true); |
460 | 390 |
461 set_id(VIEW_ID_TAB); | 391 set_id(VIEW_ID_TAB); |
462 | 392 |
463 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); | 393 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); |
464 title_->SetElideBehavior(gfx::FADE_TAIL); | 394 title_->SetElideBehavior(gfx::FADE_TAIL); |
465 title_->SetHandlesTooltips(false); | 395 title_->SetHandlesTooltips(false); |
466 title_->SetAutoColorReadabilityEnabled(false); | 396 title_->SetAutoColorReadabilityEnabled(false); |
467 title_->SetText(CoreTabHelper::GetDefaultTitle()); | 397 title_->SetText(CoreTabHelper::GetDefaultTitle()); |
468 AddChildView(title_); | 398 AddChildView(title_); |
469 | 399 |
470 SetEventTargeter( | 400 SetEventTargeter( |
471 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); | 401 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); |
472 | 402 |
473 throbber_ = new ThrobberView(this); | |
474 throbber_->SetVisible(false); | |
475 AddChildView(throbber_); | |
476 | |
477 media_indicator_button_ = new MediaIndicatorButton(this); | 403 media_indicator_button_ = new MediaIndicatorButton(this); |
478 AddChildView(media_indicator_button_); | 404 AddChildView(media_indicator_button_); |
479 | 405 |
480 close_button_ = new TabCloseButton(this); | 406 close_button_ = new TabCloseButton(this); |
481 close_button_->SetAccessibleName( | 407 close_button_->SetAccessibleName( |
482 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); | 408 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); |
483 // The normal image is set by OnButtonColorMaybeChanged() because it depends | 409 // The normal image is set by OnButtonColorMaybeChanged() because it depends |
484 // on the current theme and active state. The hovered and pressed images | 410 // on the current theme and active state. The hovered and pressed images |
485 // don't depend on the these, so we can set them here. | 411 // don't depend on the these, so we can set them here. |
486 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon( | 412 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon( |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 return controller_->IsTabSelected(this); | 447 return controller_->IsTabSelected(this); |
522 } | 448 } |
523 | 449 |
524 void Tab::SetData(const TabRendererData& data) { | 450 void Tab::SetData(const TabRendererData& data) { |
525 DCHECK(GetWidget()); | 451 DCHECK(GetWidget()); |
526 | 452 |
527 if (data_.Equals(data)) | 453 if (data_.Equals(data)) |
528 return; | 454 return; |
529 | 455 |
530 TabRendererData old(data_); | 456 TabRendererData old(data_); |
531 UpdateLoadingAnimation(data.network_state); | |
532 data_ = data; | 457 data_ = data; |
533 | 458 |
534 base::string16 title = data_.title; | 459 base::string16 title = data_.title; |
535 if (title.empty()) { | 460 if (title.empty()) { |
536 title = data_.loading ? | 461 title = data_.loading ? |
537 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : | 462 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : |
538 CoreTabHelper::GetDefaultTitle(); | 463 CoreTabHelper::GetDefaultTitle(); |
539 } else { | 464 } else { |
540 Browser::FormatTitleForDisplay(&title); | 465 Browser::FormatTitleForDisplay(&title); |
541 } | 466 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
581 } | 506 } |
582 | 507 |
583 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { | 508 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { |
584 if (state == data_.network_state && | 509 if (state == data_.network_state && |
585 state == TabRendererData::NETWORK_STATE_NONE) { | 510 state == TabRendererData::NETWORK_STATE_NONE) { |
586 // If the network state is none and hasn't changed, do nothing. Otherwise we | 511 // If the network state is none and hasn't changed, do nothing. Otherwise we |
587 // need to advance the animation frame. | 512 // need to advance the animation frame. |
588 return; | 513 return; |
589 } | 514 } |
590 | 515 |
| 516 TabRendererData::NetworkState old_state = data_.network_state; |
591 data_.network_state = state; | 517 data_.network_state = state; |
592 AdvanceLoadingAnimation(); | 518 AdvanceLoadingAnimation(old_state, state); |
593 } | 519 } |
594 | 520 |
595 void Tab::StartPulse() { | 521 void Tab::StartPulse() { |
596 pulse_animation_.reset(new gfx::ThrobAnimation(this)); | 522 pulse_animation_.reset(new gfx::ThrobAnimation(this)); |
597 pulse_animation_->SetSlideDuration(kPulseDurationMs); | 523 pulse_animation_->SetSlideDuration(kPulseDurationMs); |
598 if (animation_container_.get()) | 524 if (animation_container_.get()) |
599 pulse_animation_->SetContainer(animation_container_.get()); | 525 pulse_animation_->SetContainer(animation_container_.get()); |
600 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); | 526 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); |
601 } | 527 } |
602 | 528 |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
862 const int extra_padding = | 788 const int extra_padding = |
863 (controller_->ShouldHideCloseButtonForInactiveTabs() || | 789 (controller_->ShouldHideCloseButtonForInactiveTabs() || |
864 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; | 790 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; |
865 const int start = lb.x() + extra_padding; | 791 const int start = lb.x() + extra_padding; |
866 favicon_bounds_.SetRect(start, lb.y(), 0, 0); | 792 favicon_bounds_.SetRect(start, lb.y(), 0, 0); |
867 if (showing_icon_) { | 793 if (showing_icon_) { |
868 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); | 794 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); |
869 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); | 795 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); |
870 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); | 796 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); |
871 } | 797 } |
872 throbber_->SetBoundsRect(favicon_bounds_); | |
873 | 798 |
874 showing_close_button_ = ShouldShowCloseBox(); | 799 showing_close_button_ = ShouldShowCloseBox(); |
875 if (showing_close_button_) { | 800 if (showing_close_button_) { |
876 // If the ratio of the close button size to tab width exceeds the maximum. | 801 // If the ratio of the close button size to tab width exceeds the maximum. |
877 // The close button should be as large as possible so that there is a larger | 802 // The close button should be as large as possible so that there is a larger |
878 // hit-target for touch events. So the close button bounds extends to the | 803 // hit-target for touch events. So the close button bounds extends to the |
879 // edges of the tab. However, the larger hit-target should be active only | 804 // edges of the tab. However, the larger hit-target should be active only |
880 // for mouse events, and the close-image should show up in the right place. | 805 // for mouse events, and the close-image should show up in the right place. |
881 // So a border is added to the button with necessary padding. The close | 806 // So a border is added to the button with necessary padding. The close |
882 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target | 807 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target |
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1419 | 1344 |
1420 void Tab::PaintIcon(gfx::Canvas* canvas) { | 1345 void Tab::PaintIcon(gfx::Canvas* canvas) { |
1421 gfx::Rect bounds = favicon_bounds_; | 1346 gfx::Rect bounds = favicon_bounds_; |
1422 bounds.set_x(GetMirroredXForRect(bounds)); | 1347 bounds.set_x(GetMirroredXForRect(bounds)); |
1423 bounds.Offset(0, favicon_hiding_offset_); | 1348 bounds.Offset(0, favicon_hiding_offset_); |
1424 bounds.Intersect(GetInteriorBounds()); | 1349 bounds.Intersect(GetInteriorBounds()); |
1425 if (bounds.IsEmpty()) | 1350 if (bounds.IsEmpty()) |
1426 return; | 1351 return; |
1427 | 1352 |
1428 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { | 1353 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { |
1429 // Throbber will do its own painting. | 1354 // Paint network activity (aka throbber) animation frame. |
| 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 } |
1430 } else { | 1376 } else { |
1431 const gfx::ImageSkia& favicon = should_display_crashed_favicon_ ? | 1377 const gfx::ImageSkia& favicon = should_display_crashed_favicon_ ? |
1432 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( | 1378 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
1433 IDR_CRASH_SAD_FAVICON) : | 1379 IDR_CRASH_SAD_FAVICON) : |
1434 data().favicon; | 1380 data().favicon; |
1435 if (!favicon.isNull()) { | 1381 if (!favicon.isNull()) { |
1436 canvas->DrawImageInt(favicon, 0, 0, bounds.width(), bounds.height(), | 1382 canvas->DrawImageInt(favicon, 0, 0, bounds.width(), bounds.height(), |
1437 bounds.x(), bounds.y(), bounds.width(), | 1383 bounds.x(), bounds.y(), bounds.width(), |
1438 bounds.height(), false); | 1384 bounds.height(), false); |
1439 } | 1385 } |
1440 } | 1386 } |
1441 } | 1387 } |
1442 | 1388 |
1443 void Tab::AdvanceLoadingAnimation() { | 1389 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, |
1444 const TabRendererData::NetworkState state = data().network_state; | 1390 TabRendererData::NetworkState state) { |
| 1391 if (state == TabRendererData::NETWORK_STATE_WAITING) { |
| 1392 // Waiting steps backwards. |
| 1393 immersive_loading_step_ = |
| 1394 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % |
| 1395 kImmersiveLoadingStepCount; |
| 1396 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { |
| 1397 immersive_loading_step_ = (immersive_loading_step_ + 1) % |
| 1398 kImmersiveLoadingStepCount; |
| 1399 } else { |
| 1400 waiting_start_time_ = base::TimeTicks(); |
| 1401 loading_start_time_ = base::TimeTicks(); |
| 1402 waiting_state_ = gfx::ThrobberWaitingState(); |
| 1403 immersive_loading_step_ = 0; |
| 1404 } |
1445 if (controller_->IsImmersiveStyle()) { | 1405 if (controller_->IsImmersiveStyle()) { |
1446 if (state == TabRendererData::NETWORK_STATE_WAITING) { | |
1447 // Waiting steps backwards. | |
1448 immersive_loading_step_ = | |
1449 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % | |
1450 kImmersiveLoadingStepCount; | |
1451 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { | |
1452 immersive_loading_step_ = | |
1453 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount; | |
1454 } else { | |
1455 immersive_loading_step_ = 0; | |
1456 } | |
1457 | |
1458 SchedulePaintInRect(GetImmersiveBarRect()); | 1406 SchedulePaintInRect(GetImmersiveBarRect()); |
1459 return; | 1407 } else { |
| 1408 ScheduleIconPaint(); |
1460 } | 1409 } |
1461 | |
1462 if (state == TabRendererData::NETWORK_STATE_NONE) { | |
1463 throbber_->ResetStartTimes(); | |
1464 throbber_->SetVisible(false); | |
1465 ScheduleIconPaint(); | |
1466 return; | |
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(); | |
1483 } | 1410 } |
1484 | 1411 |
1485 int Tab::IconCapacity() const { | 1412 int Tab::IconCapacity() const { |
1486 const gfx::Size min_size(GetMinimumInactiveSize()); | 1413 const gfx::Size min_size(GetMinimumInactiveSize()); |
1487 if (height() < min_size.height()) | 1414 if (height() < min_size.height()) |
1488 return 0; | 1415 return 0; |
1489 const int available_width = std::max(0, width() - min_size.width()); | 1416 const int available_width = std::max(0, width() - min_size.width()); |
1490 // All icons are the same size as the favicon. | 1417 // All icons are the same size as the favicon. |
1491 const int icon_width = gfx::kFaviconSize; | 1418 const int icon_width = gfx::kFaviconSize; |
1492 // We need enough space to display the icons flush against each other. | 1419 // 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... |
1684 const gfx::ImageSkia& image) { | 1611 const gfx::ImageSkia& image) { |
1685 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); | 1612 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); |
1686 ImageCacheEntry entry; | 1613 ImageCacheEntry entry; |
1687 entry.resource_id = resource_id; | 1614 entry.resource_id = resource_id; |
1688 entry.scale_factor = scale_factor; | 1615 entry.scale_factor = scale_factor; |
1689 entry.image = image; | 1616 entry.image = image; |
1690 image_cache_->push_front(entry); | 1617 image_cache_->push_front(entry); |
1691 if (image_cache_->size() > kMaxImageCacheSize) | 1618 if (image_cache_->size() > kMaxImageCacheSize) |
1692 image_cache_->pop_back(); | 1619 image_cache_->pop_back(); |
1693 } | 1620 } |
OLD | NEW |