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/browser/extensions/extension_sidebar_api.h" 6 7 #include "base/json/json_writer.h" 8 #include "base/string_number_conversions.h" 9 #include "base/string_util.h" 10 #include "base/string16.h" 11 #include "base/values.h" 12 #include "chrome/browser/extensions/extension_event_router.h" 13 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/extensions/extension_tabs_module.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/sidebar/sidebar_container.h" 17 #include "chrome/browser/sidebar/sidebar_manager.h" 18 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 19 #include "chrome/common/extensions/extension.h" 20 #include "chrome/common/extensions/extension_constants.h" 21 #include "chrome/common/extensions/extension_error_utils.h" 22 #include "chrome/common/extensions/extension_sidebar_utils.h" 23 #include "chrome/common/render_messages.h" 24 #include "content/browser/tab_contents/tab_contents.h" 25 #include "ipc/ipc_message_utils.h" 26 #include "third_party/skia/include/core/SkBitmap.h" 27 28 namespace { 29 // Errors. 30 const char kNoSidebarError[] = 31 "This extension has no sidebar specified."; 32 const char kNoTabError[] = "No tab with id: *."; 33 const char kNoCurrentWindowError[] = "No current browser window was found"; 34 const char kNoDefaultTabError[] = "No default tab was found"; 35 const char kInvalidExpandContextError[] = 36 "Sidebar can be expanded only in response to an explicit user gesture"; 37 // Keys. 38 const char kBadgeTextKey[] = "text"; 39 const char kImageDataKey[] = "imageData"; 40 const char kPathKey[] = "path"; 41 const char kStateKey[] = "state"; 42 const char kTabIdKey[] = "tabId"; 43 const char kTitleKey[] = "title"; 44 // Events. 45 const char kOnStateChanged[] = "experimental.sidebar.onStateChanged"; 46 } // namespace 47 48 namespace extension_sidebar_constants { 49 // Sidebar states. 50 const char kActiveState[] = "active"; 51 const char kHiddenState[] = "hidden"; 52 const char kShownState[] = "shown"; 53 } // namespace extension_sidebar_constants 54 55 // static 56 void ExtensionSidebarEventRouter::OnStateChanged( 57 Profile* profile, TabContents* tab, const std::string& content_id, 58 const std::string& state) { 59 int tab_id = ExtensionTabUtil::GetTabId(tab); 60 DictionaryValue* details = new DictionaryValue; 61 details->Set(kTabIdKey, Value::CreateIntegerValue(tab_id)); 62 details->Set(kStateKey, Value::CreateStringValue(state)); 63 64 ListValue args; 65 args.Set(0, details); 66 std::string json_args; 67 base::JSONWriter::Write(&args, false, &json_args); 68 69 profile->GetExtensionEventRouter()->DispatchEventToExtension( 70 extension_sidebar_utils::GetExtensionIdByContentId(content_id), 71 kOnStateChanged, json_args, profile, GURL()); 72 } 73 74 75 // List is considered empty if it is actually empty or contains just one value, 76 // either 'null' or 'undefined'. 77 static bool IsArgumentListEmpty(const ListValue* arguments) { 78 if (arguments->empty()) 79 return true; 80 if (arguments->GetSize() == 1) { 81 Value* first_value = 0; 82 if (!arguments->Get(0, &first_value)) 83 return true; 84 if (first_value->GetType() == Value::TYPE_NULL) 85 return true; 86 } 87 return false; 88 } 89 90 bool SidebarFunction::RunImpl() { 91 if (!GetExtension()->sidebar_defaults()) { 92 error_ = kNoSidebarError; 93 return false; 94 } 95 96 if (!args_.get()) 97 return false; 98 99 DictionaryValue* details = NULL; 100 DictionaryValue default_details; 101 if (IsArgumentListEmpty(args_.get())) { 102 details = &default_details; 103 } else { 104 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details)); 105 } 106 107 int tab_id; 108 TabContentsWrapper* tab_contents = NULL; 109 if (details->HasKey(kTabIdKey)) { 110 EXTENSION_FUNCTION_VALIDATE(details->GetInteger(kTabIdKey, &tab_id)); 111 if (!ExtensionTabUtil::GetTabById(tab_id, profile(), include_incognito(), 112 NULL, NULL, &tab_contents, NULL)) { 113 error_ = ExtensionErrorUtils::FormatErrorMessage( 114 kNoTabError, base::IntToString(tab_id)); 115 return false; 116 } 117 } else { 118 Browser* browser = GetCurrentBrowser(); 119 if (!browser) { 120 error_ = kNoCurrentWindowError; 121 return false; 122 } 123 if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id)) { 124 error_ = kNoDefaultTabError; 125 return false; 126 } 127 } 128 if (!tab_contents) 129 return false; 130 131 std::string content_id(GetExtension()->id()); 132 return RunImpl(tab_contents->tab_contents(), content_id, *details); 133 } 134 135 136 bool CollapseSidebarFunction::RunImpl(TabContents* tab, 137 const std::string& content_id, 138 const DictionaryValue& details) { 139 SidebarManager::GetInstance()->CollapseSidebar(tab, content_id); 140 return true; 141 } 142 143 bool ExpandSidebarFunction::RunImpl(TabContents* tab, 144 const std::string& content_id, 145 const DictionaryValue& details) { 146 // TODO(alekseys): enable this check back when WebKit's user gesture flag 147 // reporting for extension calls is fixed. 148 // if (!user_gesture()) { 149 // error_ = kInvalidExpandContextError; 150 // return false; 151 // } 152 SidebarManager::GetInstance()->ExpandSidebar(tab, content_id); 153 return true; 154 } 155 156 bool GetStateSidebarFunction::RunImpl(TabContents* tab, 157 const std::string& content_id, 158 const DictionaryValue& details) { 159 SidebarManager* manager = SidebarManager::GetInstance(); 160 161 const char* result = extension_sidebar_constants::kHiddenState; 162 if (manager->GetSidebarTabContents(tab, content_id)) { 163 bool is_active = false; 164 // Sidebar is considered active only if tab is selected, sidebar UI 165 // is expanded and this extension's content is displayed on it. 166 SidebarContainer* active_sidebar = 167 manager->GetActiveSidebarContainerFor(tab); 168 // Check if sidebar UI is expanded and this extension's content 169 // is displayed on it. 170 if (active_sidebar && active_sidebar->content_id() == content_id) { 171 if (!details.HasKey(kTabIdKey)) { 172 is_active = NULL != GetCurrentBrowser(); 173 } else { 174 int tab_id; 175 EXTENSION_FUNCTION_VALIDATE(details.GetInteger(kTabIdKey, &tab_id)); 176 177 // Check if this tab is selected. 178 Browser* browser = GetCurrentBrowser(); 179 TabContentsWrapper* contents = NULL; 180 int default_tab_id = -1; 181 if (browser && 182 ExtensionTabUtil::GetDefaultTab(browser, &contents, 183 &default_tab_id)) { 184 is_active = default_tab_id == tab_id; 185 } 186 } 187 } 188 189 result = is_active ? extension_sidebar_constants::kActiveState : 190 extension_sidebar_constants::kShownState; 191 } 192 193 result_.reset(Value::CreateStringValue(result)); 194 return true; 195 } 196 197 bool HideSidebarFunction::RunImpl(TabContents* tab, 198 const std::string& content_id, 199 const DictionaryValue& details) { 200 SidebarManager::GetInstance()->HideSidebar(tab, content_id); 201 return true; 202 } 203 204 bool NavigateSidebarFunction::RunImpl(TabContents* tab, 205 const std::string& content_id, 206 const DictionaryValue& details) { 207 std::string path_string; 208 EXTENSION_FUNCTION_VALIDATE(details.GetString(kPathKey, &path_string)); 209 210 GURL url = extension_sidebar_utils::ResolveRelativePath( 211 path_string, GetExtension(), &error_); 212 if (!url.is_valid()) 213 return false; 214 215 SidebarManager::GetInstance()->NavigateSidebar(tab, content_id, url); 216 return true; 217 } 218 219 bool SetBadgeTextSidebarFunction::RunImpl(TabContents* tab, 220 const std::string& content_id, 221 const DictionaryValue& details) { 222 string16 badge_text; 223 EXTENSION_FUNCTION_VALIDATE(details.GetString(kBadgeTextKey, &badge_text)); 224 SidebarManager::GetInstance()->SetSidebarBadgeText( 225 tab, content_id, badge_text); 226 return true; 227 } 228 229 bool SetIconSidebarFunction::RunImpl(TabContents* tab, 230 const std::string& content_id, 231 const DictionaryValue& details) { 232 BinaryValue* binary; 233 EXTENSION_FUNCTION_VALIDATE(details.GetBinary(kImageDataKey, &binary)); 234 IPC::Message bitmap_pickle(binary->GetBuffer(), binary->GetSize()); 235 void* iter = NULL; 236 scoped_ptr<SkBitmap> bitmap(new SkBitmap); 237 EXTENSION_FUNCTION_VALIDATE( 238 IPC::ReadParam(&bitmap_pickle, &iter, bitmap.get())); 239 SidebarManager::GetInstance()->SetSidebarIcon(tab, content_id, *bitmap); 240 return true; 241 } 242 243 bool SetTitleSidebarFunction::RunImpl(TabContents* tab, 244 const std::string& content_id, 245 const DictionaryValue& details) { 246 string16 title; 247 EXTENSION_FUNCTION_VALIDATE(details.GetString(kTitleKey, &title)); 248 SidebarManager::GetInstance()->SetSidebarTitle(tab, content_id, title); 249 return true; 250 } 251 252 bool ShowSidebarFunction::RunImpl(TabContents* tab, 253 const std::string& content_id, 254 const DictionaryValue& details) { 255 SidebarManager::GetInstance()->ShowSidebar(tab, content_id); 256 return true; 257 } 258