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
OnStateChanged(Profile * profile,TabContents * tab,const std::string & content_id,const std::string & state)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'.
IsArgumentListEmpty(const ListValue * arguments)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
RunImpl()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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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
RunImpl(TabContents * tab,const std::string & content_id,const DictionaryValue & details)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