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
24namespace keys = extension_page_actions_module_constants;
25
26namespace {
27// Errors.
28const char kNoTabError[] = "No tab with id: *.";
29const char kNoPageActionError[] =
30    "This extension has no page action specified.";
31const char kUrlNotActiveError[] = "This url is no longer active: *.";
32const char kIconIndexOutOfBounds[] = "Page action icon index out of bounds.";
33const char kNoIconSpecified[] = "Page action has no icons to show.";
34}
35
36// TODO(EXTENSIONS_DEPRECATED): obsolete API.
37bool 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
98bool 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
120bool 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
131bool EnablePageActionFunction::RunImpl() {
132  return SetPageActionEnabled(true);
133}
134
135bool DisablePageActionFunction::RunImpl() {
136  return SetPageActionEnabled(false);
137}
138
139bool PageActionShowFunction::RunImpl() {
140  return SetVisible(true);
141}
142
143bool PageActionHideFunction::RunImpl() {
144  return SetVisible(false);
145}
146
147bool 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
183bool 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
200bool 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.
225bool 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.
251bool 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.
277bool 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