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/gtk/script_bubble_gtk.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/i18n/rtl.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/extensions/extension_system.h" 14 #include "chrome/browser/extensions/image_loader.h" 15 #include "chrome/browser/extensions/script_bubble_controller.h" 16 #include "chrome/browser/extensions/tab_helper.h" 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 19 #include "chrome/common/extensions/extension_set.h" 20 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" 21 #include "chrome/common/url_constants.h" 22 #include "content/public/browser/web_contents.h" 23 #include "extensions/common/extension.h" 24 #include "grit/generated_resources.h" 25 #include "grit/theme_resources.h" 26 #include "grit/ui_resources.h" 27 #include "ui/base/gtk/gtk_hig_constants.h" 28 #include "ui/base/l10n/l10n_util.h" 29 30 using content::WebContents; 31 using content::OpenURLParams; 32 using content::Referrer; 33 using extensions::Extension; 34 using extensions::ExtensionSystem; 35 using extensions::TabHelper; 36 using extensions::ImageLoader; 37 38 namespace { 39 40 // The currently open app-modal bubble singleton, or NULL if none is open. 41 ScriptBubbleGtk* g_bubble = NULL; 42 43 } // namespace 44 45 46 // static 47 void ScriptBubbleGtk::Show(GtkWidget* anchor, WebContents* web_contents) { 48 if (!g_bubble) 49 g_bubble = new ScriptBubbleGtk(anchor, web_contents); 50 } 51 52 // static 53 void ScriptBubbleGtk::OnItemLinkClickedThunk(GtkWidget* sender, 54 void* user_data) { 55 g_bubble->OnItemLinkClicked(sender); 56 } 57 58 ScriptBubbleGtk::ScriptBubbleGtk(GtkWidget* anchor, WebContents* web_contents) 59 : anchor_(anchor), 60 web_contents_(web_contents), 61 profile_(Profile::FromBrowserContext(web_contents_->GetBrowserContext())), 62 weak_ptr_factory_(this) { 63 BuildBubble(); 64 } 65 66 ScriptBubbleGtk::~ScriptBubbleGtk() { 67 } 68 69 void ScriptBubbleGtk::Close() { 70 if (bubble_) 71 bubble_->Close(); 72 } 73 74 void ScriptBubbleGtk::BubbleClosing(BubbleGtk* bubble, 75 bool closed_by_escape) { 76 g_bubble = NULL; 77 delete this; 78 } 79 80 void ScriptBubbleGtk::BuildBubble() { 81 GtkThemeService* theme_provider = GtkThemeService::GetFrom(profile_); 82 83 GtkWidget* bubble_content = gtk_vbox_new(FALSE, ui::kControlSpacing); 84 gtk_container_set_border_width(GTK_CONTAINER(bubble_content), 85 ui::kContentAreaBorder); 86 87 extensions::TabHelper* tab_helper = 88 extensions::TabHelper::FromWebContents(web_contents_); 89 const std::set<std::string>& extensions_running_scripts = 90 tab_helper->script_bubble_controller()->extensions_running_scripts(); 91 const ExtensionSet* loaded_extensions = 92 ExtensionSystem::Get(profile_)->extension_service()->extensions(); 93 94 std::string heading_string = 95 l10n_util::GetStringUTF8(IDS_SCRIPT_BUBBLE_HEADLINE); 96 GtkWidget* heading = gtk_label_new(heading_string.c_str()); 97 gtk_misc_set_alignment(GTK_MISC(heading), 0, 0); 98 gtk_box_pack_start(GTK_BOX(bubble_content), heading, FALSE, FALSE, 0); 99 100 for (std::set<std::string>::const_iterator iter = 101 extensions_running_scripts.begin(); 102 iter != extensions_running_scripts.end(); ++iter) { 103 const Extension* extension = loaded_extensions->GetByID(*iter); 104 if (!extension) 105 continue; 106 107 GtkWidget* item = gtk_hbox_new(FALSE, ui::kControlSpacing); 108 gtk_box_pack_start(GTK_BOX(bubble_content), item, FALSE, FALSE, 0); 109 110 GtkWidget* image = gtk_image_new(); 111 gtk_widget_set_usize(image, 16, 16); 112 gtk_box_pack_start(GTK_BOX(item), image, FALSE, FALSE, 0); 113 114 // Load the image asynchronously. 115 icon_controls_[extension->id()] = GTK_IMAGE(image); 116 ImageLoader::Get(profile_)->LoadImageAsync( 117 extension, 118 extensions::IconsInfo::GetIconResource( 119 extension, 120 extension_misc::EXTENSION_ICON_BITTY, 121 ExtensionIconSet::MATCH_EXACTLY), 122 gfx::Size(extension_misc::EXTENSION_ICON_BITTY, 123 extension_misc::EXTENSION_ICON_BITTY), 124 base::Bind(&ScriptBubbleGtk::OnIconLoaded, 125 weak_ptr_factory_.GetWeakPtr(), 126 extension->id())); 127 128 GtkWidget* link = theme_provider->BuildChromeLinkButton( 129 extension->name().c_str()); 130 gtk_box_pack_start(GTK_BOX(item), link, FALSE, FALSE, 0); 131 link_controls_[GTK_WIDGET(link)] = extension->id(); 132 g_signal_connect(link, "button-press-event", 133 G_CALLBACK(&OnItemLinkClickedThunk), this); 134 } 135 136 bubble_ = BubbleGtk::Show(anchor_, 137 NULL, 138 bubble_content, 139 BubbleGtk::ANCHOR_TOP_RIGHT, 140 BubbleGtk::MATCH_SYSTEM_THEME | 141 BubbleGtk::POPUP_WINDOW | 142 BubbleGtk::GRAB_INPUT, 143 theme_provider, 144 this); 145 } 146 147 void ScriptBubbleGtk::OnIconLoaded(const std::string& extension_id, 148 const gfx::Image& icon) { 149 gtk_image_set_from_pixbuf( 150 icon_controls_[extension_id], 151 (!icon.IsEmpty() ? icon : 152 GtkThemeService::GetFrom(profile_)->GetImageNamed( 153 IDR_EXTENSIONS_FAVICON)).ToGdkPixbuf()); 154 } 155 156 void ScriptBubbleGtk::OnItemLinkClicked(GtkWidget* button) { 157 std::string link(chrome::kChromeUIExtensionsURL); 158 link += "?id=" + link_controls_[button]; 159 web_contents_->OpenURL(OpenURLParams(GURL(link), 160 Referrer(), 161 NEW_FOREGROUND_TAB, 162 content::PAGE_TRANSITION_LINK, 163 false)); 164 Close(); 165 } 166