Home | History | Annotate | Download | only in extensions
      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