15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 11ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/badge_util.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h" 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/icon_with_badge_image_source.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/ui_resources.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h" 18424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmapDevice.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkCanvas.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkPaint.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/effects/SkGradientShader.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h" 23d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/animation_delegate.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/color_utils.h" 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/image/image.h" 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia.h" 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia_source.h" 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h" 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/size.h" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/skbitmap_operations.h" 327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h" 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class GetAttentionImageSource : public gfx::ImageSkiaSource { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) explicit GetAttentionImageSource(const gfx::ImageSkia& icon) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : icon_(icon) {} 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // gfx::ImageSkiaSource overrides: 4268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 4368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale); 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) color_utils::HSL shift = {-1, 0, 0.5}; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return gfx::ImageSkiaRep( 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkBitmapOperations::CreateHSLShiftedBitmap(icon_rep.sk_bitmap(), shift), 4768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) icon_rep.scale()); 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::ImageSkia icon_; 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(tbarzic): Merge AnimationIconImageSource and IconAnimation together. 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Source for painting animated skia image. 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class AnimatedIconImageSource : public gfx::ImageSkiaSource { 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AnimatedIconImageSource( 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::ImageSkia& image, 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::WeakPtr<ExtensionAction::IconAnimation> animation) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : image_(image), 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) animation_(animation) { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual ~AnimatedIconImageSource() {} 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::ImageSkiaRep original_rep = image_.GetRepresentation(scale); 72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!animation_.get()) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return original_rep; 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Original representation's scale factor may be different from scale 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // factor passed to this method. We want to use the former (since we are 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // using bitmap for that scale). 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return gfx::ImageSkiaRep( 7968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) animation_->Apply(original_rep.sk_bitmap()), original_rep.scale()); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::ImageSkia image_; 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::WeakPtr<ExtensionAction::IconAnimation> animation_; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(AnimatedIconImageSource); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int ExtensionAction::kDefaultTabId = -1; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 100ms animation at 50fps (so 5 animation frames in total). 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kIconFadeInDurationMs = 100; 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kIconFadeInFramesPerSecond = 50; 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction::IconAnimation::IconAnimation() 94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) : gfx::LinearAnimation(kIconFadeInDurationMs, kIconFadeInFramesPerSecond, 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL), 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) weak_ptr_factory_(this) {} 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction::IconAnimation::~IconAnimation() { 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make sure observers don't access *this after its destructor has started. 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) weak_ptr_factory_.InvalidateWeakPtrs(); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // In case the animation was destroyed before it finished (likely due to 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // delays in timer scheduling), make sure it's fully visible. 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FOR_EACH_OBSERVER(Observer, observers_, OnIconChanged()); 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const SkBitmap& ExtensionAction::IconAnimation::Apply( 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const SkBitmap& icon) const { 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GT(icon.width(), 0); 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GT(icon.height(), 0); 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!device_.get() || 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (device_->width() != icon.width()) || 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (device_->height() != icon.height())) { 114424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) device_.reset(new SkBitmapDevice( 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkBitmap::kARGB_8888_Config, icon.width(), icon.height(), true)); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkCanvas canvas(device_.get()); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.clear(SK_ColorWHITE); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkPaint paint; 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) paint.setAlpha(CurrentValueBetween(0, 255)); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.drawBitmap(icon, 0, 0, &paint); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return device_->accessBitmap(false); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)base::WeakPtr<ExtensionAction::IconAnimation> 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction::IconAnimation::AsWeakPtr() { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return weak_ptr_factory_.GetWeakPtr(); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::IconAnimation::AddObserver( 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ExtensionAction::IconAnimation::Observer* observer) { 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) observers_.AddObserver(observer); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::IconAnimation::RemoveObserver( 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ExtensionAction::IconAnimation::Observer* observer) { 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) observers_.RemoveObserver(observer); 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::IconAnimation::AnimateToState(double state) { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FOR_EACH_OBSERVER(Observer, observers_, OnIconChanged()); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction::IconAnimation::ScopedObserver::ScopedObserver( 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::WeakPtr<IconAnimation>& icon_animation, 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Observer* observer) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : icon_animation_(icon_animation), 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) observer_(observer) { 150868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (icon_animation.get()) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation->AddObserver(observer); 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction::IconAnimation::ScopedObserver::~ScopedObserver() { 155868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (icon_animation_.get()) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation_->RemoveObserver(observer_); 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction::ExtensionAction( 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& extension_id, 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extensions::ActionInfo::Type action_type, 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const extensions::ActionInfo& manifest_data) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : extension_id_(extension_id), 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) action_type_(action_type), 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) has_changed_(false) { 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Page/script actions are hidden/disabled by default, and browser actions are 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // visible/enabled by default. 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetAppearance(kDefaultTabId, 1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) action_type == extensions::ActionInfo::TYPE_BROWSER ? 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetTitle(kDefaultTabId, manifest_data.default_title); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!manifest_data.default_icon.empty()) { 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_default_icon(make_scoped_ptr(new ExtensionIconSet( 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) manifest_data.default_icon))); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_id(manifest_data.id); 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction::~ExtensionAction() { 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)scoped_ptr<ExtensionAction> ExtensionAction::CopyForTest() const { 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<ExtensionAction> copy( 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new ExtensionAction(extension_id_, action_type_, 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extensions::ActionInfo())); 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->popup_url_ = popup_url_; 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->title_ = title_; 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->icon_ = icon_; 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->badge_text_ = badge_text_; 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->badge_background_color_ = badge_background_color_; 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->badge_text_color_ = badge_text_color_; 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->appearance_ = appearance_; 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->icon_animation_ = icon_animation_; 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->id_ = id_; 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (default_icon_) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy->default_icon_.reset(new ExtensionIconSet(*default_icon_)); 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return copy.Pass(); 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionAction::GetIconSizeForType( 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extensions::ActionInfo::Type type) { 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (type) { 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case extensions::ActionInfo::TYPE_BROWSER: 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case extensions::ActionInfo::TYPE_PAGE: 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case extensions::ActionInfo::TYPE_SYSTEM_INDICATOR: 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // TODO(dewittj) Report the actual icon size of the system 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // indicator. 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return extension_misc::EXTENSION_ICON_ACTION; 2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case extensions::ActionInfo::TYPE_SCRIPT_BADGE: 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return extension_misc::EXTENSION_ICON_BITTY; 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) default: 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) { 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We store |url| even if it is empty, rather than removing a URL from the 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // map. If an extension has a default popup, and removes it for a tab via 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the API, we must remember that there is no popup for that specific tab. 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we removed the tab's URL, GetPopupURL would incorrectly return the 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // default URL. 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetValue(&popup_url_, tab_id, url); 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ExtensionAction::HasPopup(int tab_id) const { 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return !GetPopupUrl(tab_id).is_empty(); 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL ExtensionAction::GetPopupUrl(int tab_id) const { 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetValue(&popup_url_, tab_id); 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::SetIcon(int tab_id, const gfx::Image& image) { 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetValue(&icon_, tab_id, image.AsImageSkia()); 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Image ExtensionAction::ApplyAttentionAndAnimation( 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::ImageSkia& original_icon, 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int tab_id) const { 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::ImageSkia icon = original_icon; 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (GetValue(&appearance_, tab_id) == WANTS_ATTENTION) 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon = gfx::ImageSkia(new GetAttentionImageSource(icon), icon.size()); 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return gfx::Image(ApplyIconAnimation(tab_id, icon)); 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::ImageSkia ExtensionAction::GetExplicitlySetIcon(int tab_id) const { 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetValue(&icon_, tab_id); 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ExtensionAction::SetAppearance(int tab_id, Appearance new_appearance) { 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Appearance old_appearance = GetValue(&appearance_, tab_id); 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (old_appearance == new_appearance) 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetValue(&appearance_, tab_id, new_appearance); 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // When showing a script badge for the first time on a web page, fade it in. 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Other transitions happen instantly. 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (old_appearance == INVISIBLE && tab_id != kDefaultTabId && 2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) action_type_ == extensions::ActionInfo::TYPE_SCRIPT_BADGE) { 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) RunIconAnimation(tab_id); 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionAction::DeclarativeShow(int tab_id) { 2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK_NE(tab_id, kDefaultTabId); 2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ++declarative_show_count_[tab_id]; // Use default initialization to 0. 2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionAction::UndoDeclarativeShow(int tab_id) { 2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int& show_count = declarative_show_count_[tab_id]; 2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK_GT(show_count, 0); 2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (--show_count == 0) 2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) declarative_show_count_.erase(tab_id); 2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::ClearAllValuesForTab(int tab_id) { 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) popup_url_.erase(tab_id); 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) title_.erase(tab_id); 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_.erase(tab_id); 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) badge_text_.erase(tab_id); 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) badge_text_color_.erase(tab_id); 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) badge_background_color_.erase(tab_id); 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) appearance_.erase(tab_id); 2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // TODO(jyasskin): Erase the element from declarative_show_count_ 2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // when the tab's closed. There's a race between the 2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // PageActionController and the ContentRulesRegistry on navigation, 2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // which prevents me from cleaning everything up now. 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation_.erase(tab_id); 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::PaintBadge(gfx::Canvas* canvas, 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::Rect& bounds, 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int tab_id) { 3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) badge_util::PaintBadge( 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas, 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bounds, 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetBadgeText(tab_id), 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetBadgeTextColor(tab_id), 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetBadgeBackgroundColor(tab_id), 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetIconWidth(tab_id), 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) action_type()); 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::ImageSkia ExtensionAction::GetIconWithBadge( 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::ImageSkia& icon, 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int tab_id, 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::Size& spacing) const { 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (tab_id < 0) 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return icon; 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return gfx::ImageSkia( 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new IconWithBadgeImageSource(icon, 3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) icon.size(), 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) spacing, 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetBadgeText(tab_id), 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetBadgeTextColor(tab_id), 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetBadgeBackgroundColor(tab_id), 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) action_type()), 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon.size()); 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Determines which icon would be returned by |GetIcon|, and returns its width. 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionAction::GetIconWidth(int tab_id) const { 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If icon has been set, return its width. 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::ImageSkia icon = GetValue(&icon_, tab_id); 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!icon.isNull()) 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return icon.width(); 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If there is a default icon, the icon width will be set depending on our 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // action type. 340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (default_icon_) 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetIconSizeForType(action_type()); 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If no icon has been set and there is no default icon, we need favicon 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // width. 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ui::ResourceBundle::GetSharedInstance().GetImageNamed( 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IDR_EXTENSIONS_FAVICON).ToImageSkia()->width(); 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)base::WeakPtr<ExtensionAction::IconAnimation> ExtensionAction::GetIconAnimation( 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int tab_id) const { 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::map<int, base::WeakPtr<IconAnimation> >::iterator it = 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation_.find(tab_id); 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it == icon_animation_.end()) 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return base::WeakPtr<ExtensionAction::IconAnimation>(); 355868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (it->second.get()) 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return it->second; 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Take this opportunity to remove all the NULL IconAnimations from 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // icon_animation_. 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation_.erase(it); 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (it = icon_animation_.begin(); it != icon_animation_.end();) { 362868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (it->second.get()) { 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++it; 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The WeakPtr is null; remove it from the map. 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation_.erase(it++); 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return base::WeakPtr<ExtensionAction::IconAnimation>(); 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::ImageSkia ExtensionAction::ApplyIconAnimation( 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int tab_id, 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::ImageSkia& icon) const { 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::WeakPtr<IconAnimation> animation = GetIconAnimation(tab_id); 376868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (animation.get() == NULL) 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return icon; 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return gfx::ImageSkia(new AnimatedIconImageSource(icon, animation), 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon.size()); 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Used to create a Callback owning an IconAnimation. 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DestroyIconAnimation(scoped_ptr<ExtensionAction::IconAnimation>) {} 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionAction::RunIconAnimation(int tab_id) { 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<IconAnimation> icon_animation(new IconAnimation()); 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation_[tab_id] = icon_animation->AsWeakPtr(); 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_animation->Start(); 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // After the icon is finished fading in (plus some padding to handle random 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // timer delays), destroy it. We use a delayed task so that the Animation is 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // deleted even if it hasn't finished by the time the MessageLoop is 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // destroyed. 39590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::MessageLoop::current()->PostDelayedTask( 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, 3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::Bind(&DestroyIconAnimation, base::Passed(&icon_animation)), 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::TimeDelta::FromMilliseconds(kIconFadeInDurationMs * 2)); 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 400