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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/script_bubble_view.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/browser/extensions/extension_service.h" 9 #include "chrome/browser/extensions/extension_system.h" 10 #include "chrome/browser/extensions/image_loader.h" 11 #include "chrome/browser/extensions/script_bubble_controller.h" 12 #include "chrome/browser/extensions/tab_helper.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" 15 #include "chrome/common/url_constants.h" 16 #include "content/public/browser/page_navigator.h" 17 #include "content/public/browser/web_contents.h" 18 #include "extensions/common/extension.h" 19 #include "extensions/common/extension_resource.h" 20 #include "grit/generated_resources.h" 21 #include "grit/locale_settings.h" 22 #include "ui/base/l10n/l10n_util.h" 23 #include "ui/views/controls/image_view.h" 24 #include "ui/views/controls/label.h" 25 #include "ui/views/controls/link.h" 26 #include "ui/views/layout/grid_layout.h" 27 #include "ui/views/widget/widget.h" 28 29 using content::OpenURLParams; 30 using content::Referrer; 31 using content::WebContents; 32 using extensions::Extension; 33 using extensions::ExtensionSystem; 34 using extensions::ScriptBubbleController; 35 36 namespace { 37 38 // Layout constants. 39 const int kInsetTop = 7; 40 const int kInsetLeft = 14; 41 const int kInsetBottom = 11; 42 const int kPaddingBelowHeadline = 12; 43 const int kPaddingRightOfIcon = 8; 44 const int kPaddingBetweenRows = 13; 45 46 } // namespace 47 48 //////////////////////////////////////////////////////////////////////////////// 49 // ScriptBubbleView 50 51 ScriptBubbleView::ScriptEntry::ScriptEntry() 52 : extension_imageview(NULL) {} 53 54 ScriptBubbleView::ScriptBubbleView(views::View* anchor_view, 55 WebContents* web_contents) 56 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT), 57 height_(0), 58 web_contents_(web_contents) { 59 // Compensate for built-in vertical padding in the anchor view's image. 60 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0)); 61 62 extensions::ScriptBubbleController* script_bubble_controller = 63 GetScriptBubbleController(); 64 const std::set<std::string>& extensions_running_scripts = 65 script_bubble_controller->extensions_running_scripts(); 66 Profile* profile = Profile::FromBrowserContext( 67 web_contents_->GetBrowserContext()); 68 ExtensionService* extension_service = 69 ExtensionSystem::Get(profile)->extension_service(); 70 71 size_t i = 0; 72 for (std::set<std::string>::const_iterator iter = 73 extensions_running_scripts.begin(); 74 iter != extensions_running_scripts.end(); ++iter, ++i) { 75 // We specify |true| to get disabled extensions as well, since content 76 // scripts survive their extensions being disabled. 77 const Extension* extension = 78 extension_service->GetExtensionById(*iter, true); 79 ScriptEntry entry; 80 entry.extension_id = *iter; 81 entry.extension_name = UTF8ToUTF16(extension->name()); 82 entries_.push_back(entry); 83 84 int size = extension_misc::EXTENSION_ICON_BITTY; 85 extensions::ExtensionResource image = 86 extensions::IconsInfo::GetIconResource(extension, 87 size, 88 ExtensionIconSet::MATCH_BIGGER); 89 extensions::ImageLoader::Get(profile)->LoadImageAsync( 90 extension, image, gfx::Size(size, size), 91 base::Bind(&ScriptBubbleView::OnImageLoaded, AsWeakPtr(), i)); 92 } 93 } 94 95 ScriptBubbleView::~ScriptBubbleView() { 96 } 97 98 gfx::Size ScriptBubbleView::GetPreferredSize() { 99 gfx::Size size(views::Widget::GetLocalizedContentsSize( 100 IDS_SCRIPTBUBBLE_WIDTH_CHARS, 101 IDS_SCRIPTBUBBLE_HEIGHT_LINES)); 102 size.set_height(std::max(size.height(), height_)); 103 return size; 104 } 105 106 void ScriptBubbleView::LinkClicked(views::Link* source, int event_flags) { 107 std::string link(chrome::kChromeUIExtensionsURL); 108 link += std::string("?id=") + 109 entries_[source->id()].extension_id; 110 web_contents_->OpenURL(OpenURLParams(GURL(link), 111 Referrer(), 112 NEW_FOREGROUND_TAB, 113 content::PAGE_TRANSITION_LINK, 114 false)); 115 } 116 117 void ScriptBubbleView::Init() { 118 height_ = 0; 119 120 views::GridLayout* layout = new views::GridLayout(this); 121 SetLayoutManager(layout); 122 layout->SetInsets(kInsetTop, kInsetLeft, 0, 0); 123 height_ = kInsetTop + kInsetBottom; 124 125 // Add a column for the heading (one large text column). 126 views::ColumnSet* columns = layout->AddColumnSet(0); 127 columns->AddColumn(views::GridLayout::LEADING, // Horizontal resize. 128 views::GridLayout::FILL, // Vertical resize. 129 1, // Resize weight. 130 views::GridLayout::USE_PREF, // Size type. 131 0, // Ignored for USE_PREF. 132 0); // Minimum size. 133 134 // Add a column set for the extension image plus name. 135 columns = layout->AddColumnSet(1); 136 columns->AddColumn(views::GridLayout::LEADING, // Horizontal resize. 137 views::GridLayout::FILL, // Vertical resize. 138 0, // Resize weight. 139 views::GridLayout::USE_PREF, // Size type. 140 0, // Ignored for USE_PREF. 141 0); // Minimum size. 142 143 columns->AddPaddingColumn(0, // resize_percent. 144 kPaddingRightOfIcon); // width. 145 146 columns->AddColumn(views::GridLayout::LEADING, // Horizontal resize. 147 views::GridLayout::FILL, // Vertical resize. 148 1, // Resize weight. 149 views::GridLayout::USE_PREF, // Size type. 150 0, // Ignored for USE_PREF. 151 0); // Minimum size. 152 153 layout->StartRow(0, 0); 154 views::Label* heading = new views::Label( 155 l10n_util::GetStringUTF16(IDS_SCRIPT_BUBBLE_HEADLINE)); 156 layout->AddView(heading); 157 height_ += heading->GetPreferredSize().height(); 158 159 layout->AddPaddingRow(0, kPaddingBelowHeadline); 160 height_ += kPaddingBelowHeadline; 161 162 for (size_t i = 0; i < entries_.size(); ++i) { 163 layout->StartRow(0, 1); 164 165 views::ImageView* image_view = new views::ImageView(); 166 entries_[i].extension_imageview = image_view; 167 image_view->SetImageSize(gfx::Size(16, 16)); 168 image_view->SetImage( 169 extensions::IconsInfo::GetDefaultExtensionIcon()); 170 layout->AddView(image_view); 171 172 views::Link* link = new views::Link(entries_[i].extension_name); 173 link->set_id(i); 174 link->set_listener(this); 175 layout->AddView(link); 176 177 height_ += std::max(image_view->GetPreferredSize().height(), 178 link->GetPreferredSize().height()); 179 180 if (i + 1 < entries_.size()) { 181 layout->AddPaddingRow(0, kPaddingBetweenRows); 182 height_ += kPaddingBetweenRows; 183 } 184 } 185 186 layout->Layout(this); 187 } 188 189 void ScriptBubbleView::OnImageLoaded(size_t index, 190 const gfx::Image& image) { 191 if (!image.IsEmpty()) { 192 const gfx::ImageSkia* image_skia = image.ToImageSkia(); 193 entries_[index].extension_imageview->SetImage(image_skia); 194 } 195 } 196 197 ScriptBubbleController* ScriptBubbleView::GetScriptBubbleController() { 198 extensions::TabHelper* extensions_tab_helper = 199 extensions::TabHelper::FromWebContents(web_contents_); 200 return extensions_tab_helper->script_bubble_controller(); 201 } 202