1 // Copyright (c) 2011 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_page_actions_module.h"
6
7 #include <string>
8
9 #include "base/string_number_conversions.h"
10 #include "chrome/browser/extensions/extension_page_actions_module_constants.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_tab_helper.h"
13 #include "chrome/browser/extensions/extension_tabs_module.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser_list.h"
16 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "chrome/common/extensions/extension_action.h"
19 #include "chrome/common/extensions/extension_error_utils.h"
20 #include "chrome/common/render_messages.h"
21 #include "content/browser/tab_contents/navigation_entry.h"
22 #include "content/browser/tab_contents/tab_contents.h"
23
24 namespace keys = extension_page_actions_module_constants;
25
26 namespace {
27 // Errors.
28 const char kNoTabError[] = "No tab with id: *.";
29 const char kNoPageActionError[] =
30 "This extension has no page action specified.";
31 const char kUrlNotActiveError[] = "This url is no longer active: *.";
32 const char kIconIndexOutOfBounds[] = "Page action icon index out of bounds.";
33 const char kNoIconSpecified[] = "Page action has no icons to show.";
34 }
35
36 // TODO(EXTENSIONS_DEPRECATED): obsolete API.
SetPageActionEnabled(bool enable)37 bool PageActionFunction::SetPageActionEnabled(bool enable) {
38 std::string page_action_id;
39 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &page_action_id));
40 DictionaryValue* action;
41 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &action));
42
43 int tab_id;
44 EXTENSION_FUNCTION_VALIDATE(action->GetInteger(keys::kTabIdKey, &tab_id));
45 std::string url;
46 EXTENSION_FUNCTION_VALIDATE(action->GetString(keys::kUrlKey, &url));
47
48 std::string title;
49 int icon_id = 0;
50 if (enable) {
51 // Both of those are optional.
52 if (action->HasKey(keys::kTitleKey))
53 EXTENSION_FUNCTION_VALIDATE(action->GetString(keys::kTitleKey, &title));
54 if (action->HasKey(keys::kIconIdKey)) {
55 EXTENSION_FUNCTION_VALIDATE(action->GetInteger(keys::kIconIdKey,
56 &icon_id));
57 }
58 }
59
60 ExtensionAction* page_action = GetExtension()->page_action();
61 if (!page_action) {
62 error_ = kNoPageActionError;
63 return false;
64 }
65
66 if (icon_id < 0 ||
67 static_cast<size_t>(icon_id) >= page_action->icon_paths()->size()) {
68 error_ = (icon_id == 0) ? kNoIconSpecified : kIconIndexOutOfBounds;
69 return false;
70 }
71
72 // Find the TabContents that contains this tab id.
73 TabContentsWrapper* contents = NULL;
74 bool result = ExtensionTabUtil::GetTabById(
75 tab_id, profile(), include_incognito(), NULL, NULL, &contents, NULL);
76 if (!result || !contents) {
77 error_ = ExtensionErrorUtils::FormatErrorMessage(
78 kNoTabError, base::IntToString(tab_id));
79 return false;
80 }
81
82 // Make sure the URL hasn't changed.
83 NavigationEntry* entry = contents->controller().GetActiveEntry();
84 if (!entry || url != entry->url().spec()) {
85 error_ = ExtensionErrorUtils::FormatErrorMessage(kUrlNotActiveError, url);
86 return false;
87 }
88
89 // Set visibility and broadcast notifications that the UI should be updated.
90 page_action->SetIsVisible(tab_id, enable);
91 page_action->SetTitle(tab_id, title);
92 page_action->SetIconIndex(tab_id, icon_id);
93 contents->extension_tab_helper()->PageActionStateChanged();
94
95 return true;
96 }
97
InitCommon(int tab_id)98 bool PageActionFunction::InitCommon(int tab_id) {
99 page_action_ = GetExtension()->page_action();
100 if (!page_action_) {
101 error_ = kNoPageActionError;
102 return false;
103 }
104
105 // Find the TabContents that contains this tab id.
106 contents_ = NULL;
107 TabContentsWrapper* wrapper = NULL;
108 bool result = ExtensionTabUtil::GetTabById(
109 tab_id, profile(), include_incognito(), NULL, NULL, &wrapper, NULL);
110 if (!result || !wrapper) {
111 error_ = ExtensionErrorUtils::FormatErrorMessage(
112 kNoTabError, base::IntToString(tab_id));
113 return false;
114 }
115 contents_ = wrapper;
116
117 return true;
118 }
119
SetVisible(bool visible)120 bool PageActionFunction::SetVisible(bool visible) {
121 int tab_id;
122 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
123 if (!InitCommon(tab_id))
124 return false;
125
126 page_action_->SetIsVisible(tab_id, visible);
127 contents_->extension_tab_helper()->PageActionStateChanged();
128 return true;
129 }
130
RunImpl()131 bool EnablePageActionFunction::RunImpl() {
132 return SetPageActionEnabled(true);
133 }
134
RunImpl()135 bool DisablePageActionFunction::RunImpl() {
136 return SetPageActionEnabled(false);
137 }
138
RunImpl()139 bool PageActionShowFunction::RunImpl() {
140 return SetVisible(true);
141 }
142
RunImpl()143 bool PageActionHideFunction::RunImpl() {
144 return SetVisible(false);
145 }
146
RunImpl()147 bool PageActionSetIconFunction::RunImpl() {
148 DictionaryValue* args;
149 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
150
151 int tab_id;
152 EXTENSION_FUNCTION_VALIDATE(args->GetInteger("tabId", &tab_id));
153 if (!InitCommon(tab_id))
154 return false;
155
156 // setIcon can take a variant argument: either a canvas ImageData, or an
157 // icon index.
158 BinaryValue* binary;
159 int icon_index;
160 if (args->GetBinary("imageData", &binary)) {
161 IPC::Message bitmap_pickle(binary->GetBuffer(), binary->GetSize());
162 void* iter = NULL;
163 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
164 EXTENSION_FUNCTION_VALIDATE(
165 IPC::ReadParam(&bitmap_pickle, &iter, bitmap.get()));
166 page_action_->SetIcon(tab_id, *bitmap);
167 } else if (args->GetInteger("iconIndex", &icon_index)) {
168 if (icon_index < 0 || static_cast<size_t>(icon_index) >=
169 page_action_->icon_paths()->size()) {
170 error_ = kIconIndexOutOfBounds;
171 return false;
172 }
173 page_action_->SetIcon(tab_id, SkBitmap());
174 page_action_->SetIconIndex(tab_id, icon_index);
175 } else {
176 EXTENSION_FUNCTION_VALIDATE(false);
177 }
178
179 contents_->extension_tab_helper()->PageActionStateChanged();
180 return true;
181 }
182
RunImpl()183 bool PageActionSetTitleFunction::RunImpl() {
184 DictionaryValue* args;
185 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
186
187 int tab_id;
188 EXTENSION_FUNCTION_VALIDATE(args->GetInteger("tabId", &tab_id));
189 if (!InitCommon(tab_id))
190 return false;
191
192 std::string title;
193 EXTENSION_FUNCTION_VALIDATE(args->GetString("title", &title));
194
195 page_action_->SetTitle(tab_id, title);
196 contents_->extension_tab_helper()->PageActionStateChanged();
197 return true;
198 }
199
RunImpl()200 bool PageActionSetPopupFunction::RunImpl() {
201 DictionaryValue* args;
202 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
203
204 int tab_id;
205 EXTENSION_FUNCTION_VALIDATE(args->GetInteger("tabId", &tab_id));
206 if (!InitCommon(tab_id))
207 return false;
208
209 // TODO(skerner): Consider allowing null and undefined to mean the popup
210 // should be removed.
211 std::string popup_string;
212 EXTENSION_FUNCTION_VALIDATE(args->GetString("popup", &popup_string));
213
214 GURL popup_url;
215 if (!popup_string.empty())
216 popup_url = GetExtension()->GetResourceURL(popup_string);
217
218 page_action_->SetPopupUrl(tab_id, popup_url);
219 contents_->extension_tab_helper()->PageActionStateChanged();
220 return true;
221 }
222
223 // Not currently exposed to extensions. To re-enable, add mapping in
224 // extension_function_dispatcher.
RunImpl()225 bool PageActionSetBadgeBackgroundColorFunction::RunImpl() {
226 DictionaryValue* args;
227 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
228
229 int tab_id;
230 EXTENSION_FUNCTION_VALIDATE(args->GetInteger("tabId", &tab_id));
231 if (!InitCommon(tab_id))
232 return false;
233
234 ListValue* color_value;
235 EXTENSION_FUNCTION_VALIDATE(args->GetList("color", &color_value));
236 EXTENSION_FUNCTION_VALIDATE(color_value->GetSize() == 4);
237
238 int color_array[4] = {0};
239 for (size_t i = 0; i < arraysize(color_array); ++i)
240 EXTENSION_FUNCTION_VALIDATE(color_value->GetInteger(i, &color_array[i]));
241
242 SkColor color = SkColorSetARGB(color_array[3], color_array[0], color_array[1],
243 color_array[2]);
244 page_action_->SetBadgeBackgroundColor(tab_id, color);
245 contents_->extension_tab_helper()->PageActionStateChanged();
246 return true;
247 }
248
249 // Not currently exposed to extensions. To re-enable, add mapping in
250 // extension_function_dispatcher.
RunImpl()251 bool PageActionSetBadgeTextColorFunction::RunImpl() {
252 DictionaryValue* args;
253 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
254
255 int tab_id;
256 EXTENSION_FUNCTION_VALIDATE(args->GetInteger("tabId", &tab_id));
257 if (!InitCommon(tab_id))
258 return false;
259
260 ListValue* color_value;
261 EXTENSION_FUNCTION_VALIDATE(args->GetList("color", &color_value));
262 EXTENSION_FUNCTION_VALIDATE(color_value->GetSize() == 4);
263
264 int color_array[4] = {0};
265 for (size_t i = 0; i < arraysize(color_array); ++i)
266 EXTENSION_FUNCTION_VALIDATE(color_value->GetInteger(i, &color_array[i]));
267
268 SkColor color = SkColorSetARGB(color_array[3], color_array[0], color_array[1],
269 color_array[2]);
270 page_action_->SetBadgeTextColor(tab_id, color);
271 contents_->extension_tab_helper()->PageActionStateChanged();
272 return true;
273 }
274
275 // Not currently exposed to extensions. To re-enable, add mapping in
276 // extension_function_dispatcher.
RunImpl()277 bool PageActionSetBadgeTextFunction::RunImpl() {
278 DictionaryValue* args;
279 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
280
281 int tab_id;
282 EXTENSION_FUNCTION_VALIDATE(args->GetInteger("tabId", &tab_id));
283 if (!InitCommon(tab_id))
284 return false;
285
286 std::string text;
287 EXTENSION_FUNCTION_VALIDATE(args->GetString("text", &text));
288
289 page_action_->SetBadgeText(tab_id, text);
290 contents_->extension_tab_helper()->PageActionStateChanged();
291 return true;
292 }
293