18ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Copyright (c) 2010 The Chromium Authors. All rights reserved. 28ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Use of this source code is governed by a BSD-style license that can be 38ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// found in the LICENSE file. 48ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 58ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "chrome/common/extensions/extension_action.h" 68ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 78ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include <algorithm> 88ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 98ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "base/logging.h" 10201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/common/badge_util.h" 118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "googleurl/src/gurl.h" 128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "grit/app_resources.h" 138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "third_party/skia/include/core/SkBitmap.h" 148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#include "third_party/skia/include/effects/SkGradientShader.h" 1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h" 1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h" 1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/rect.h" 188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsennamespace { 208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Different platforms need slightly different constants to look good. 228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#if defined(OS_LINUX) && !defined(TOOLKIT_VIEWS) 238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst float kTextSize = 9.0; 248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kBottomMargin = 0; 258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kPadding = 2; 268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kTopTextPadding = 0; 278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#elif defined(OS_LINUX) && defined(TOOLKIT_VIEWS) 288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst float kTextSize = 8.0; 298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kBottomMargin = 5; 308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kPadding = 2; 318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kTopTextPadding = 1; 328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#elif defined(OS_MACOSX) 338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst float kTextSize = 9.0; 348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kBottomMargin = 5; 358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kPadding = 2; 368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kTopTextPadding = 0; 378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#else 388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst float kTextSize = 10; 398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kBottomMargin = 5; 408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kPadding = 2; 418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// The padding between the top of the badge and the top of the text. 428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kTopTextPadding = -1; 438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen#endif 448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kBadgeHeight = 11; 468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kMaxTextWidth = 23; 478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// The minimum width for center-aligning the badge. 488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int kCenterAlignThreshold = 20; 498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} // namespace 528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenconst int ExtensionAction::kDefaultTabId = -1; 548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 558ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenExtensionAction::ExtensionAction() { 568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 588ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenExtensionAction::~ExtensionAction() { 598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) { 628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // We store |url| even if it is empty, rather than removing a URL from the 638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // map. If an extension has a default popup, and removes it for a tab via 648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // the API, we must remember that there is no popup for that specific tab. 658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // If we removed the tab's URL, GetPopupURL would incorrectly return the 668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // default URL. 678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SetValue(&popup_url_, tab_id, url); 688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenbool ExtensionAction::HasPopup(int tab_id) { 718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return !GetPopupUrl(tab_id).is_empty(); 728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 748ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenGURL ExtensionAction::GetPopupUrl(int tab_id) { 758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return GetValue(&popup_url_, tab_id); 768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid ExtensionAction::SetIcon(int tab_id, const SkBitmap& bitmap) { 798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SetValue(&icon_, tab_id, bitmap); 808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 828ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenSkBitmap ExtensionAction::GetIcon(int tab_id) { 838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return GetValue(&icon_, tab_id); 848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid ExtensionAction::SetIconIndex(int tab_id, int index) { 878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (static_cast<size_t>(index) >= icon_paths_.size()) { 888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen NOTREACHED(); 898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return; 908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SetValue(&icon_index_, tab_id, index); 928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid ExtensionAction::ClearAllValuesForTab(int tab_id) { 958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen title_.erase(tab_id); 968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen icon_.erase(tab_id); 978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen icon_index_.erase(tab_id); 988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen badge_text_.erase(tab_id); 998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen badge_text_color_.erase(tab_id); 1008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen badge_background_color_.erase(tab_id); 1018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen visible_.erase(tab_id); 1028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen popup_url_.erase(tab_id); 1038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 1048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvoid ExtensionAction::PaintBadge(gfx::Canvas* canvas, 1068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen const gfx::Rect& bounds, 1078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen int tab_id) { 1088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen std::string text = GetBadgeText(tab_id); 1098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (text.empty()) 1108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return; 1118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkColor text_color = GetBadgeTextColor(tab_id); 1138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkColor background_color = GetBadgeBackgroundColor(tab_id); 1148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (SkColorGetA(text_color) == 0x00) 1168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen text_color = SK_ColorWHITE; 1178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (SkColorGetA(background_color) == 0x00) 1198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen background_color = SkColorSetARGB(255, 218, 0, 24); // Default badge color. 1208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->Save(); 1228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 123201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch SkPaint* text_paint = badge_util::GetBadgeTextPaintSingleton(); 124201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch text_paint->setTextSize(SkFloatToScalar(kTextSize)); 1258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen text_paint->setColor(text_color); 1268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // Calculate text width. We clamp it to a max size. 1288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkScalar text_width = text_paint->measureText(text.c_str(), text.size()); 1298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen text_width = SkIntToScalar( 1308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen std::min(kMaxTextWidth, SkScalarFloor(text_width))); 1318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // Calculate badge size. It is clamped to a min width just because it looks 1338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // silly if it is too skinny. 1348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen int badge_width = SkScalarFloor(text_width) + kPadding * 2; 1358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen int icon_width = GetIcon(tab_id).width(); 1368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // Force the pixel width of badge to be either odd (if the icon width is odd) 1378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // or even otherwise. If there is a mismatch you get http://crbug.com/26400. 1388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (icon_width != 0 && (badge_width % 2 != GetIcon(tab_id).width() % 2)) 1398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen badge_width += 1; 1408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen badge_width = std::max(kBadgeHeight, badge_width); 1418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // Paint the badge background color in the right location. It is usually 1438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // right-aligned, but it can also be center-aligned if it is large. 1448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkRect rect; 1458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fBottom = SkIntToScalar(bounds.bottom() - kBottomMargin); 1468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fTop = rect.fBottom - SkIntToScalar(kBadgeHeight); 1478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (badge_width >= kCenterAlignThreshold) { 1488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fLeft = SkIntToScalar( 1498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkScalarFloor(SkIntToScalar(bounds.x()) + 1508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkIntToScalar(bounds.width() / 2.0) - 1518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkIntToScalar(badge_width / 2.0))); 1528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fRight = rect.fLeft + SkIntToScalar(badge_width); 1538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } else { 1548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fRight = SkIntToScalar(bounds.right()); 1558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fLeft = rect.fRight - badge_width; 1568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkPaint rect_paint; 1598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect_paint.setStyle(SkPaint::kFill_Style); 1608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect_paint.setAntiAlias(true); 1618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect_paint.setColor(background_color); 1628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->AsCanvasSkia()->drawRoundRect(rect, SkIntToScalar(2), 1638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkIntToScalar(2), rect_paint); 1648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // Overlay the gradient. It is stretchy, so we do this in three parts. 1668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ResourceBundle& resource_bundle = ResourceBundle::GetSharedInstance(); 1678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkBitmap* gradient_left = resource_bundle.GetBitmapNamed( 1688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen IDR_BROWSER_ACTION_BADGE_LEFT); 1698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkBitmap* gradient_right = resource_bundle.GetBitmapNamed( 1708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen IDR_BROWSER_ACTION_BADGE_RIGHT); 1718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkBitmap* gradient_center = resource_bundle.GetBitmapNamed( 1728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen IDR_BROWSER_ACTION_BADGE_CENTER); 1738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->AsCanvasSkia()->drawBitmap(*gradient_left, rect.fLeft, rect.fTop); 1758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->TileImageInt(*gradient_center, 1768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkScalarFloor(rect.fLeft) + gradient_left->width(), 1778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkScalarFloor(rect.fTop), 1788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkScalarFloor(rect.width()) - gradient_left->width() - 1798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen gradient_right->width(), 1808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen SkScalarFloor(rect.height())); 1818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->AsCanvasSkia()->drawBitmap(*gradient_right, 1828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fRight - SkIntToScalar(gradient_right->width()), rect.fTop); 1838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // Finally, draw the text centered within the badge. We set a clip in case the 1858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // text was too large. 1868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fLeft += kPadding; 1878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fRight -= kPadding; 1888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->AsCanvasSkia()->clipRect(rect); 1898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->AsCanvasSkia()->drawText(text.c_str(), text.size(), 1908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fLeft + (rect.width() - text_width) / 2, 1918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen rect.fTop + kTextSize + kTopTextPadding, 1928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *text_paint); 1938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen canvas->Restore(); 1948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 195