// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_sidebar_api.h" #include "base/json/json_writer.h" #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/string16.h" #include "base/values.h" #include "chrome/browser/extensions/extension_event_router.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sidebar/sidebar_container.h" #include "chrome/browser/sidebar/sidebar_manager.h" #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/extensions/extension_sidebar_utils.h" #include "chrome/common/render_messages.h" #include "content/browser/tab_contents/tab_contents.h" #include "ipc/ipc_message_utils.h" #include "third_party/skia/include/core/SkBitmap.h" namespace { // Errors. const char kNoSidebarError[] = "This extension has no sidebar specified."; const char kNoTabError[] = "No tab with id: *."; const char kNoCurrentWindowError[] = "No current browser window was found"; const char kNoDefaultTabError[] = "No default tab was found"; const char kInvalidExpandContextError[] = "Sidebar can be expanded only in response to an explicit user gesture"; // Keys. const char kBadgeTextKey[] = "text"; const char kImageDataKey[] = "imageData"; const char kPathKey[] = "path"; const char kStateKey[] = "state"; const char kTabIdKey[] = "tabId"; const char kTitleKey[] = "title"; // Events. const char kOnStateChanged[] = "experimental.sidebar.onStateChanged"; } // namespace namespace extension_sidebar_constants { // Sidebar states. const char kActiveState[] = "active"; const char kHiddenState[] = "hidden"; const char kShownState[] = "shown"; } // namespace extension_sidebar_constants // static void ExtensionSidebarEventRouter::OnStateChanged( Profile* profile, TabContents* tab, const std::string& content_id, const std::string& state) { int tab_id = ExtensionTabUtil::GetTabId(tab); DictionaryValue* details = new DictionaryValue; details->Set(kTabIdKey, Value::CreateIntegerValue(tab_id)); details->Set(kStateKey, Value::CreateStringValue(state)); ListValue args; args.Set(0, details); std::string json_args; base::JSONWriter::Write(&args, false, &json_args); profile->GetExtensionEventRouter()->DispatchEventToExtension( extension_sidebar_utils::GetExtensionIdByContentId(content_id), kOnStateChanged, json_args, profile, GURL()); } // List is considered empty if it is actually empty or contains just one value, // either 'null' or 'undefined'. static bool IsArgumentListEmpty(const ListValue* arguments) { if (arguments->empty()) return true; if (arguments->GetSize() == 1) { Value* first_value = 0; if (!arguments->Get(0, &first_value)) return true; if (first_value->GetType() == Value::TYPE_NULL) return true; } return false; } bool SidebarFunction::RunImpl() { if (!GetExtension()->sidebar_defaults()) { error_ = kNoSidebarError; return false; } if (!args_.get()) return false; DictionaryValue* details = NULL; DictionaryValue default_details; if (IsArgumentListEmpty(args_.get())) { details = &default_details; } else { EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details)); } int tab_id; TabContentsWrapper* tab_contents = NULL; if (details->HasKey(kTabIdKey)) { EXTENSION_FUNCTION_VALIDATE(details->GetInteger(kTabIdKey, &tab_id)); if (!ExtensionTabUtil::GetTabById(tab_id, profile(), include_incognito(), NULL, NULL, &tab_contents, NULL)) { error_ = ExtensionErrorUtils::FormatErrorMessage( kNoTabError, base::IntToString(tab_id)); return false; } } else { Browser* browser = GetCurrentBrowser(); if (!browser) { error_ = kNoCurrentWindowError; return false; } if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id)) { error_ = kNoDefaultTabError; return false; } } if (!tab_contents) return false; std::string content_id(GetExtension()->id()); return RunImpl(tab_contents->tab_contents(), content_id, *details); } bool CollapseSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { SidebarManager::GetInstance()->CollapseSidebar(tab, content_id); return true; } bool ExpandSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { // TODO(alekseys): enable this check back when WebKit's user gesture flag // reporting for extension calls is fixed. // if (!user_gesture()) { // error_ = kInvalidExpandContextError; // return false; // } SidebarManager::GetInstance()->ExpandSidebar(tab, content_id); return true; } bool GetStateSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { SidebarManager* manager = SidebarManager::GetInstance(); const char* result = extension_sidebar_constants::kHiddenState; if (manager->GetSidebarTabContents(tab, content_id)) { bool is_active = false; // Sidebar is considered active only if tab is selected, sidebar UI // is expanded and this extension's content is displayed on it. SidebarContainer* active_sidebar = manager->GetActiveSidebarContainerFor(tab); // Check if sidebar UI is expanded and this extension's content // is displayed on it. if (active_sidebar && active_sidebar->content_id() == content_id) { if (!details.HasKey(kTabIdKey)) { is_active = NULL != GetCurrentBrowser(); } else { int tab_id; EXTENSION_FUNCTION_VALIDATE(details.GetInteger(kTabIdKey, &tab_id)); // Check if this tab is selected. Browser* browser = GetCurrentBrowser(); TabContentsWrapper* contents = NULL; int default_tab_id = -1; if (browser && ExtensionTabUtil::GetDefaultTab(browser, &contents, &default_tab_id)) { is_active = default_tab_id == tab_id; } } } result = is_active ? extension_sidebar_constants::kActiveState : extension_sidebar_constants::kShownState; } result_.reset(Value::CreateStringValue(result)); return true; } bool HideSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { SidebarManager::GetInstance()->HideSidebar(tab, content_id); return true; } bool NavigateSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { std::string path_string; EXTENSION_FUNCTION_VALIDATE(details.GetString(kPathKey, &path_string)); GURL url = extension_sidebar_utils::ResolveRelativePath( path_string, GetExtension(), &error_); if (!url.is_valid()) return false; SidebarManager::GetInstance()->NavigateSidebar(tab, content_id, url); return true; } bool SetBadgeTextSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { string16 badge_text; EXTENSION_FUNCTION_VALIDATE(details.GetString(kBadgeTextKey, &badge_text)); SidebarManager::GetInstance()->SetSidebarBadgeText( tab, content_id, badge_text); return true; } bool SetIconSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { BinaryValue* binary; EXTENSION_FUNCTION_VALIDATE(details.GetBinary(kImageDataKey, &binary)); IPC::Message bitmap_pickle(binary->GetBuffer(), binary->GetSize()); void* iter = NULL; scoped_ptr bitmap(new SkBitmap); EXTENSION_FUNCTION_VALIDATE( IPC::ReadParam(&bitmap_pickle, &iter, bitmap.get())); SidebarManager::GetInstance()->SetSidebarIcon(tab, content_id, *bitmap); return true; } bool SetTitleSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { string16 title; EXTENSION_FUNCTION_VALIDATE(details.GetString(kTitleKey, &title)); SidebarManager::GetInstance()->SetSidebarTitle(tab, content_id, title); return true; } bool ShowSidebarFunction::RunImpl(TabContents* tab, const std::string& content_id, const DictionaryValue& details) { SidebarManager::GetInstance()->ShowSidebar(tab, content_id); return true; }