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 3 // found in the LICENSE file. 4 5 #include "chrome/common/extensions/extension_action.h" 6 7 #include <algorithm> 8 9 #include "base/logging.h" 10 #include "chrome/common/badge_util.h" 11 #include "googleurl/src/gurl.h" 12 #include "grit/app_resources.h" 13 #include "third_party/skia/include/core/SkBitmap.h" 14 #include "third_party/skia/include/effects/SkGradientShader.h" 15 #include "ui/base/resource/resource_bundle.h" 16 #include "ui/gfx/canvas_skia.h" 17 #include "ui/gfx/rect.h" 18 19 namespace { 20 21 // Different platforms need slightly different constants to look good. 22 #if defined(OS_LINUX) && !defined(TOOLKIT_VIEWS) 23 const float kTextSize = 9.0; 24 const int kBottomMargin = 0; 25 const int kPadding = 2; 26 const int kTopTextPadding = 0; 27 #elif defined(OS_LINUX) && defined(TOOLKIT_VIEWS) 28 const float kTextSize = 8.0; 29 const int kBottomMargin = 5; 30 const int kPadding = 2; 31 const int kTopTextPadding = 1; 32 #elif defined(OS_MACOSX) 33 const float kTextSize = 9.0; 34 const int kBottomMargin = 5; 35 const int kPadding = 2; 36 const int kTopTextPadding = 0; 37 #else 38 const float kTextSize = 10; 39 const int kBottomMargin = 5; 40 const int kPadding = 2; 41 // The padding between the top of the badge and the top of the text. 42 const int kTopTextPadding = -1; 43 #endif 44 45 const int kBadgeHeight = 11; 46 const int kMaxTextWidth = 23; 47 // The minimum width for center-aligning the badge. 48 const int kCenterAlignThreshold = 20; 49 50 51 } // namespace 52 53 const int ExtensionAction::kDefaultTabId = -1; 54 55 ExtensionAction::ExtensionAction() { 56 } 57 58 ExtensionAction::~ExtensionAction() { 59 } 60 61 void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) { 62 // We store |url| even if it is empty, rather than removing a URL from the 63 // map. If an extension has a default popup, and removes it for a tab via 64 // the API, we must remember that there is no popup for that specific tab. 65 // If we removed the tab's URL, GetPopupURL would incorrectly return the 66 // default URL. 67 SetValue(&popup_url_, tab_id, url); 68 } 69 70 bool ExtensionAction::HasPopup(int tab_id) { 71 return !GetPopupUrl(tab_id).is_empty(); 72 } 73 74 GURL ExtensionAction::GetPopupUrl(int tab_id) { 75 return GetValue(&popup_url_, tab_id); 76 } 77 78 void ExtensionAction::SetIcon(int tab_id, const SkBitmap& bitmap) { 79 SetValue(&icon_, tab_id, bitmap); 80 } 81 82 SkBitmap ExtensionAction::GetIcon(int tab_id) { 83 return GetValue(&icon_, tab_id); 84 } 85 86 void ExtensionAction::SetIconIndex(int tab_id, int index) { 87 if (static_cast<size_t>(index) >= icon_paths_.size()) { 88 NOTREACHED(); 89 return; 90 } 91 SetValue(&icon_index_, tab_id, index); 92 } 93 94 void ExtensionAction::ClearAllValuesForTab(int tab_id) { 95 title_.erase(tab_id); 96 icon_.erase(tab_id); 97 icon_index_.erase(tab_id); 98 badge_text_.erase(tab_id); 99 badge_text_color_.erase(tab_id); 100 badge_background_color_.erase(tab_id); 101 visible_.erase(tab_id); 102 popup_url_.erase(tab_id); 103 } 104 105 void ExtensionAction::PaintBadge(gfx::Canvas* canvas, 106 const gfx::Rect& bounds, 107 int tab_id) { 108 std::string text = GetBadgeText(tab_id); 109 if (text.empty()) 110 return; 111 112 SkColor text_color = GetBadgeTextColor(tab_id); 113 SkColor background_color = GetBadgeBackgroundColor(tab_id); 114 115 if (SkColorGetA(text_color) == 0x00) 116 text_color = SK_ColorWHITE; 117 118 if (SkColorGetA(background_color) == 0x00) 119 background_color = SkColorSetARGB(255, 218, 0, 24); // Default badge color. 120 121 canvas->Save(); 122 123 SkPaint* text_paint = badge_util::GetBadgeTextPaintSingleton(); 124 text_paint->setTextSize(SkFloatToScalar(kTextSize)); 125 text_paint->setColor(text_color); 126 127 // Calculate text width. We clamp it to a max size. 128 SkScalar text_width = text_paint->measureText(text.c_str(), text.size()); 129 text_width = SkIntToScalar( 130 std::min(kMaxTextWidth, SkScalarFloor(text_width))); 131 132 // Calculate badge size. It is clamped to a min width just because it looks 133 // silly if it is too skinny. 134 int badge_width = SkScalarFloor(text_width) + kPadding * 2; 135 int icon_width = GetIcon(tab_id).width(); 136 // Force the pixel width of badge to be either odd (if the icon width is odd) 137 // or even otherwise. If there is a mismatch you get http://crbug.com/26400. 138 if (icon_width != 0 && (badge_width % 2 != GetIcon(tab_id).width() % 2)) 139 badge_width += 1; 140 badge_width = std::max(kBadgeHeight, badge_width); 141 142 // Paint the badge background color in the right location. It is usually 143 // right-aligned, but it can also be center-aligned if it is large. 144 SkRect rect; 145 rect.fBottom = SkIntToScalar(bounds.bottom() - kBottomMargin); 146 rect.fTop = rect.fBottom - SkIntToScalar(kBadgeHeight); 147 if (badge_width >= kCenterAlignThreshold) { 148 rect.fLeft = SkIntToScalar( 149 SkScalarFloor(SkIntToScalar(bounds.x()) + 150 SkIntToScalar(bounds.width() / 2.0) - 151 SkIntToScalar(badge_width / 2.0))); 152 rect.fRight = rect.fLeft + SkIntToScalar(badge_width); 153 } else { 154 rect.fRight = SkIntToScalar(bounds.right()); 155 rect.fLeft = rect.fRight - badge_width; 156 } 157 158 SkPaint rect_paint; 159 rect_paint.setStyle(SkPaint::kFill_Style); 160 rect_paint.setAntiAlias(true); 161 rect_paint.setColor(background_color); 162 canvas->AsCanvasSkia()->drawRoundRect(rect, SkIntToScalar(2), 163 SkIntToScalar(2), rect_paint); 164 165 // Overlay the gradient. It is stretchy, so we do this in three parts. 166 ResourceBundle& resource_bundle = ResourceBundle::GetSharedInstance(); 167 SkBitmap* gradient_left = resource_bundle.GetBitmapNamed( 168 IDR_BROWSER_ACTION_BADGE_LEFT); 169 SkBitmap* gradient_right = resource_bundle.GetBitmapNamed( 170 IDR_BROWSER_ACTION_BADGE_RIGHT); 171 SkBitmap* gradient_center = resource_bundle.GetBitmapNamed( 172 IDR_BROWSER_ACTION_BADGE_CENTER); 173 174 canvas->AsCanvasSkia()->drawBitmap(*gradient_left, rect.fLeft, rect.fTop); 175 canvas->TileImageInt(*gradient_center, 176 SkScalarFloor(rect.fLeft) + gradient_left->width(), 177 SkScalarFloor(rect.fTop), 178 SkScalarFloor(rect.width()) - gradient_left->width() - 179 gradient_right->width(), 180 SkScalarFloor(rect.height())); 181 canvas->AsCanvasSkia()->drawBitmap(*gradient_right, 182 rect.fRight - SkIntToScalar(gradient_right->width()), rect.fTop); 183 184 // Finally, draw the text centered within the badge. We set a clip in case the 185 // text was too large. 186 rect.fLeft += kPadding; 187 rect.fRight -= kPadding; 188 canvas->AsCanvasSkia()->clipRect(rect); 189 canvas->AsCanvasSkia()->drawText(text.c_str(), text.size(), 190 rect.fLeft + (rect.width() - text_width) / 2, 191 rect.fTop + kTextSize + kTopTextPadding, 192 *text_paint); 193 canvas->Restore(); 194 } 195