// 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<SkBitmap> 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;
}