summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralekseys@chromium.org <alekseys@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-20 20:30:06 +0000
committeralekseys@chromium.org <alekseys@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-20 20:30:06 +0000
commite2dffe003add45048a608aef0de27784efc72e34 (patch)
tree80129fa801b1e792283c971bf33f0c941a14ca0d
parent90dba0763114f7f07d24e751cfd1c6bba0480dad (diff)
downloadchromium_src-e2dffe003add45048a608aef0de27784efc72e34.zip
chromium_src-e2dffe003add45048a608aef0de27784efc72e34.tar.gz
chromium_src-e2dffe003add45048a608aef0de27784efc72e34.tar.bz2
Add "sidebar" section to extension manifest:
"sidebar": { "default_url": "", "default_title": "", "default_icon": "" } Allow extension to access experimental.sidebar API only in presence of this section. BUG=51084 TEST=unit_tests, ExtensionManifestTest.Sidebar Review URL: http://codereview.chromium.org/6321006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71993 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_sidebar_api.cc61
-rw-r--r--chrome/browser/sidebar/sidebar_browsertest.cc (renamed from chrome/browser/sidebar/sidebar_test.cc)43
-rw-r--r--chrome/browser/sidebar/sidebar_container.cc60
-rw-r--r--chrome/browser/sidebar/sidebar_container.h32
-rw-r--r--chrome/browser/sidebar/sidebar_manager.cc3
-rw-r--r--chrome/browser/sidebar/sidebar_manager.h12
-rw-r--r--chrome/chrome_common.gypi3
-rw-r--r--chrome/chrome_tests.gypi4
-rw-r--r--chrome/common/extensions/extension.cc66
-rw-r--r--chrome/common/extensions/extension.h13
-rw-r--r--chrome/common/extensions/extension_constants.cc15
-rw-r--r--chrome/common/extensions/extension_constants.h9
-rw-r--r--chrome/common/extensions/extension_manifests_unittest.cc43
-rw-r--r--chrome/common/extensions/extension_sidebar_defaults.h48
-rw-r--r--chrome/common/extensions/extension_sidebar_utils.cc64
-rw-r--r--chrome/common/extensions/extension_sidebar_utils.h29
-rw-r--r--chrome/test/data/extensions/api_test/sidebar/manifest.json1
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar.json12
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_external_url.json10
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_icon_empty.json11
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_icon_invalid_type.json11
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_no_permissions.json9
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_title_invalid_type.json11
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_url_invalid.json12
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_url_invalid_type.json12
-rw-r--r--chrome/test/data/extensions/manifest_tests/sidebar_url_no_permissions.json12
-rw-r--r--chrome/test/data/sidebar/manifest.json7
27 files changed, 549 insertions, 64 deletions
diff --git a/chrome/browser/extensions/extension_sidebar_api.cc b/chrome/browser/extensions/extension_sidebar_api.cc
index bd32d5a..9639a3a 100644
--- a/chrome/browser/extensions/extension_sidebar_api.cc
+++ b/chrome/browser/extensions/extension_sidebar_api.cc
@@ -20,15 +20,16 @@
#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 "chrome/common/url_constants.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 kInvalidUrlError[] = "Invalid url: \"*\".";
const char kNoCurrentWindowError[] = "No current browser window was found";
const char kNoDefaultTabError[] = "No default tab was found";
const char kInvalidExpandContextError[] =
@@ -49,31 +50,7 @@ namespace extension_sidebar_constants {
const char kActiveState[] = "active";
const char kHiddenState[] = "hidden";
const char kShownState[] = "shown";
-}
-
-static GURL ResolvePossiblyRelativeURL(const std::string& url_string,
- const Extension* extension) {
- GURL url = GURL(url_string);
- if (!url.is_valid())
- url = extension->GetResourceURL(url_string);
-
- return url;
-}
-
-static bool CanUseHost(const Extension* extension,
- const GURL& url,
- std::string* error) {
- if (extension->HasHostPermission(url))
- return true;
-
- if (error) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- extension_manifest_errors::kCannotAccessPage, url.spec());
- }
-
- return false;
-}
-
+} // namespace extension_sidebar_constants
// static
void ExtensionSidebarEventRouter::OnStateChanged(
@@ -89,9 +66,9 @@ void ExtensionSidebarEventRouter::OnStateChanged(
std::string json_args;
base::JSONWriter::Write(&args, false, &json_args);
- const std::string& extension_id(content_id);
profile->GetExtensionEventRouter()->DispatchEventToExtension(
- extension_id, kOnStateChanged, json_args, profile, GURL());
+ extension_sidebar_utils::GetExtensionIdByContentId(content_id),
+ kOnStateChanged, json_args, profile, GURL());
}
@@ -111,6 +88,11 @@ static bool IsArgumentListEmpty(const ListValue* arguments) {
}
bool SidebarFunction::RunImpl() {
+ if (!GetExtension()->sidebar_defaults()) {
+ error_ = kNoSidebarError;
+ return false;
+ }
+
if (!args_.get())
return false;
@@ -224,24 +206,13 @@ bool NavigateSidebarFunction::RunImpl(TabContents* tab,
const DictionaryValue& details) {
std::string url_string;
EXTENSION_FUNCTION_VALIDATE(details.GetString(kUrlKey, &url_string));
- GURL url = ResolvePossiblyRelativeURL(url_string, GetExtension());
- if (!url.is_valid()) {
- error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidUrlError,
- url_string);
- return false;
- }
- if (!url.SchemeIs(chrome::kExtensionScheme) &&
- !CanUseHost(GetExtension(), url, &error_)) {
+
+ GURL url = extension_sidebar_utils::ResolveAndVerifyUrl(
+ url_string, GetExtension(), &error_);
+ if (!url.is_valid())
return false;
- }
- // Disallow requests outside of the requesting extension view's extension.
- if (url.SchemeIs(chrome::kExtensionScheme)) {
- std::string extension_id(url.host());
- if (extension_id != GetExtension()->id())
- return false;
- }
- SidebarManager::GetInstance()->NavigateSidebar(tab, content_id, GURL(url));
+ SidebarManager::GetInstance()->NavigateSidebar(tab, content_id, url);
return true;
}
diff --git a/chrome/browser/sidebar/sidebar_test.cc b/chrome/browser/sidebar/sidebar_browsertest.cc
index 1d57333..041a683 100644
--- a/chrome/browser/sidebar/sidebar_test.cc
+++ b/chrome/browser/sidebar/sidebar_browsertest.cc
@@ -3,30 +3,52 @@
// found in the LICENSE file.
#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/ref_counted.h"
#include "chrome/browser/browser_window.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/sidebar/sidebar_manager.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "net/test/test_server.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+
namespace {
-const char kSampleContentId[] = "sample_content_id";
const char kSimplePage[] = "files/sidebar/simple_page.html";
-class SidebarTest : public InProcessBrowserTest {
+class SidebarTest : public ExtensionBrowserTest {
public:
SidebarTest() {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalExtensionApis);
- set_show_window(true);
}
protected:
+ // InProcessBrowserTest overrides.
+ virtual void SetUpOnMainThread() {
+ ExtensionBrowserTest::SetUpOnMainThread();
+
+ // Load test sidebar extension.
+ FilePath extension_path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extension_path));
+ extension_path = extension_path.AppendASCII("sidebar");
+
+ ASSERT_TRUE(LoadExtension(extension_path));
+
+ // For now content_id == extension_id.
+ content_id_ = last_loaded_extension_id_;
+ }
+
void ShowSidebarForCurrentTab() {
ShowSidebar(browser()->GetSelectedTabContents());
}
@@ -50,10 +72,10 @@ class SidebarTest : public InProcessBrowserTest {
SidebarManager* sidebar_manager = SidebarManager::GetInstance();
- sidebar_manager->NavigateSidebar(tab, kSampleContentId, url);
+ sidebar_manager->NavigateSidebar(tab, content_id_, url);
SidebarContainer* sidebar_container =
- sidebar_manager->GetSidebarContainerFor(tab, kSampleContentId);
+ sidebar_manager->GetSidebarContainerFor(tab, content_id_);
TabContents* client_contents = sidebar_container->sidebar_contents();
ui_test_utils::WaitForNavigation(&client_contents->controller());
@@ -61,26 +83,26 @@ class SidebarTest : public InProcessBrowserTest {
void ShowSidebar(TabContents* tab) {
SidebarManager* sidebar_manager = SidebarManager::GetInstance();
- sidebar_manager->ShowSidebar(tab, kSampleContentId);
+ sidebar_manager->ShowSidebar(tab, content_id_);
}
void ExpandSidebar(TabContents* tab) {
SidebarManager* sidebar_manager = SidebarManager::GetInstance();
- sidebar_manager->ExpandSidebar(tab, kSampleContentId);
+ sidebar_manager->ExpandSidebar(tab, content_id_);
if (browser()->GetSelectedTabContents() == tab)
EXPECT_GT(browser_view()->GetSidebarWidth(), 0);
}
void CollapseSidebar(TabContents* tab) {
SidebarManager* sidebar_manager = SidebarManager::GetInstance();
- sidebar_manager->CollapseSidebar(tab, kSampleContentId);
+ sidebar_manager->CollapseSidebar(tab, content_id_);
if (browser()->GetSelectedTabContents() == tab)
EXPECT_EQ(0, browser_view()->GetSidebarWidth());
}
void HideSidebar(TabContents* tab) {
SidebarManager* sidebar_manager = SidebarManager::GetInstance();
- sidebar_manager->HideSidebar(tab, kSampleContentId);
+ sidebar_manager->HideSidebar(tab, content_id_);
if (browser()->GetSelectedTabContents() == tab)
EXPECT_EQ(0, browser_view()->GetSidebarWidth());
}
@@ -92,6 +114,9 @@ class SidebarTest : public InProcessBrowserTest {
BrowserView* browser_view() const {
return static_cast<BrowserView*>(browser()->window());
}
+
+ private:
+ std::string content_id_;
};
IN_PROC_BROWSER_TEST_F(SidebarTest, OpenClose) {
diff --git a/chrome/browser/sidebar/sidebar_container.cc b/chrome/browser/sidebar/sidebar_container.cc
index 7112433..a28ef21 100644
--- a/chrome/browser/sidebar/sidebar_container.cc
+++ b/chrome/browser/sidebar/sidebar_container.cc
@@ -4,12 +4,18 @@
#include "chrome/browser/sidebar/sidebar_container.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/common/bindings_policy.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_resource.h"
+#include "chrome/common/extensions/extension_sidebar_defaults.h"
+#include "chrome/common/extensions/extension_sidebar_utils.h"
#include "googleurl/src/gurl.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -19,7 +25,9 @@ SidebarContainer::SidebarContainer(TabContents* tab,
: tab_(tab),
content_id_(content_id),
delegate_(delegate),
- icon_(new SkBitmap) {
+ icon_(new SkBitmap),
+ navigate_to_default_url_on_expand_(true),
+ use_default_icon_(true) {
// Create TabContents for sidebar.
sidebar_contents_.reset(
new TabContents(tab->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL));
@@ -36,11 +44,41 @@ void SidebarContainer::SidebarClosing() {
delegate_->UpdateSidebar(this);
}
+void SidebarContainer::LoadDefaults() {
+ const Extension* extension = GetExtension();
+ if (!extension)
+ return; // Can be NULL in tests.
+ const ExtensionSidebarDefaults* sidebar_defaults =
+ extension->sidebar_defaults();
+
+ title_ = sidebar_defaults->default_title();
+
+ if (!sidebar_defaults->default_icon_path().empty()) {
+ image_loading_tracker_.reset(new ImageLoadingTracker(this));
+ image_loading_tracker_->LoadImage(
+ extension,
+ extension->GetResource(sidebar_defaults->default_icon_path()),
+ gfx::Size(Extension::kSidebarIconMaxSize,
+ Extension::kSidebarIconMaxSize),
+ ImageLoadingTracker::CACHE);
+ }
+}
+
void SidebarContainer::Show() {
delegate_->UpdateSidebar(this);
}
void SidebarContainer::Expand() {
+ if (navigate_to_default_url_on_expand_) {
+ navigate_to_default_url_on_expand_ = false;
+ // Check whether a default URL is specified for this sidebar.
+ const Extension* extension = GetExtension();
+ if (extension) { // Can be NULL in tests.
+ if (extension->sidebar_defaults()->default_url().is_valid())
+ Navigate(extension->sidebar_defaults()->default_url());
+ }
+ }
+
delegate_->UpdateSidebar(this);
sidebar_contents_->view()->SetInitialFocus();
}
@@ -50,8 +88,8 @@ void SidebarContainer::Collapse() {
}
void SidebarContainer::Navigate(const GURL& url) {
- DCHECK(sidebar_contents_.get());
// TODO(alekseys): add a progress UI.
+ navigate_to_default_url_on_expand_ = false;
sidebar_contents_->controller().LoadURL(
url, GURL(), PageTransition::START_PAGE);
}
@@ -61,6 +99,7 @@ void SidebarContainer::SetBadgeText(const string16& badge_text) {
}
void SidebarContainer::SetIcon(const SkBitmap& bitmap) {
+ use_default_icon_ = false;
*icon_ = bitmap;
}
@@ -72,3 +111,20 @@ bool SidebarContainer::IsPopup(const TabContents* source) const {
return false;
}
+void SidebarContainer::OnImageLoaded(SkBitmap* image,
+ ExtensionResource resource,
+ int index) {
+ if (image && use_default_icon_) {
+ *icon_ = *image;
+ delegate_->UpdateSidebar(this);
+ }
+}
+
+const Extension* SidebarContainer::GetExtension() const {
+ ExtensionService* service =
+ sidebar_contents_->profile()->GetExtensionService();
+ if (!service)
+ return NULL;
+ return service->GetExtensionById(
+ extension_sidebar_utils::GetExtensionIdByContentId(content_id_), false);
+}
diff --git a/chrome/browser/sidebar/sidebar_container.h b/chrome/browser/sidebar/sidebar_container.h
index b519de8..bcd5cfd 100644
--- a/chrome/browser/sidebar/sidebar_container.h
+++ b/chrome/browser/sidebar/sidebar_container.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
+#include "chrome/browser/extensions/image_loading_tracker.h"
#include "chrome/browser/tab_contents/tab_contents_delegate.h"
class BrowserWindow;
@@ -25,7 +26,8 @@ class TabContents;
// tab it is linked to, mini tab icon, title etc.
//
class SidebarContainer
- : public TabContentsDelegate {
+ : public TabContentsDelegate,
+ private ImageLoadingTracker::Observer {
public:
// Interface to implement to listen for sidebar update notification.
class Delegate {
@@ -37,7 +39,8 @@ class SidebarContainer
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
- SidebarContainer(TabContents* tab, const std::string& content_id,
+ SidebarContainer(TabContents* tab,
+ const std::string& content_id,
Delegate* delegate);
virtual ~SidebarContainer();
@@ -45,6 +48,9 @@ class SidebarContainer
// Does all the necessary cleanup.
void SidebarClosing();
+ // Sets default sidebar parameters, as specified in extension manifest.
+ void LoadDefaults();
+
// Returns sidebar's content id.
const std::string& content_id() const { return content_id_; }
@@ -110,6 +116,14 @@ class SidebarContainer
virtual void UpdateTargetURL(TabContents* source, const GURL& url) {}
virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {}
+ // Overridden from ImageLoadingTracker::Observer.
+ virtual void OnImageLoaded(SkBitmap* image,
+ ExtensionResource resource,
+ int index);
+
+ // Returns an extension this sidebar belongs to.
+ const Extension* GetExtension() const;
+
// Contents of the tab this sidebar is linked to.
TabContents* tab_;
@@ -132,6 +146,20 @@ class SidebarContainer
// Sidebar's title, displayed as a tooltip for sidebar's mini tab.
string16 title_;
+ // On the first expand sidebar will be automatically navigated to the default
+ // url (specified in the extension manifest), but only if the extension has
+ // not explicitly navigated it yet. This variable is set to false on the first
+ // sidebar navigation.
+ bool navigate_to_default_url_on_expand_;
+ // Since the default icon (specified in the extension manifest) is loaded
+ // asynchronously, sidebar icon can already be set by the extension
+ // by the time it's loaded. This variable tracks whether the loaded default
+ // icon should be used or discarded.
+ bool use_default_icon_;
+
+ // Helper to load icons from extension asynchronously.
+ scoped_ptr<ImageLoadingTracker> image_loading_tracker_;
+
DISALLOW_COPY_AND_ASSIGN(SidebarContainer);
};
diff --git a/chrome/browser/sidebar/sidebar_manager.cc b/chrome/browser/sidebar/sidebar_manager.cc
index 7030da8..27d7e33 100644
--- a/chrome/browser/sidebar/sidebar_manager.cc
+++ b/chrome/browser/sidebar/sidebar_manager.cc
@@ -107,6 +107,9 @@ void SidebarManager::ShowSidebar(TabContents* tab,
if (!host) {
host = new SidebarContainer(tab, content_id, this);
RegisterSidebarContainerFor(tab, host);
+ // It might trigger UpdateSidebar notification, so load them after
+ // the registration.
+ host->LoadDefaults();
}
host->Show();
diff --git a/chrome/browser/sidebar/sidebar_manager.h b/chrome/browser/sidebar/sidebar_manager.h
index 6f2d20b..e1a0080 100644
--- a/chrome/browser/sidebar/sidebar_manager.h
+++ b/chrome/browser/sidebar/sidebar_manager.h
@@ -74,19 +74,23 @@ class SidebarManager : public NotificationObserver,
void HideSidebar(TabContents* tab, const std::string& content_id);
// Navigates sidebar identified by |tab| and |content_id| to |url|.
- void NavigateSidebar(TabContents* tab, const std::string& content_id,
+ void NavigateSidebar(TabContents* tab,
+ const std::string& content_id,
const GURL& url);
// Changes sidebar's badge text (displayed on the mini tab).
- void SetSidebarBadgeText(TabContents* tab, const std::string& content_id,
+ void SetSidebarBadgeText(TabContents* tab,
+ const std::string& content_id,
const string16& badge_text);
// Changes sidebar's icon (displayed on the mini tab).
- void SetSidebarIcon(TabContents* tab, const std::string& content_id,
+ void SetSidebarIcon(TabContents* tab,
+ const std::string& content_id,
const SkBitmap& bitmap);
// Changes sidebar's title (mini tab's tooltip).
- void SetSidebarTitle(TabContents* tab, const std::string& content_id,
+ void SetSidebarTitle(TabContents* tab,
+ const std::string& content_id,
const string16& title);
private:
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 7d8d62d..5563d98 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -277,6 +277,9 @@
'common/extensions/extension_message_bundle.h',
'common/extensions/extension_resource.cc',
'common/extensions/extension_resource.h',
+ 'common/extensions/extension_sidebar_defaults.h',
+ 'common/extensions/extension_sidebar_utils.cc',
+ 'common/extensions/extension_sidebar_utils.h',
'common/extensions/extension_unpacker.cc',
'common/extensions/extension_unpacker.h',
'common/extensions/update_manifest.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index d6e50d9..d9274b1 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -444,8 +444,6 @@
'browser/accessibility/accessibility_win_browsertest.cc',
'browser/accessibility/browser_views_accessibility_browsertest.cc',
- # TODO: port sidebar.
- 'browser/sidebar/sidebar_test.cc',
],
'conditions': [
['win_use_allocator_shim==1', {
@@ -2224,6 +2222,8 @@
'<(SHARED_INTERMEDIATE_DIR)/chrome/theme_resources.rc',
'<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
'<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_resources.rc',
+ # TODO(alekseys): port sidebar to linux/mac.
+ 'browser/sidebar/sidebar_browsertest.cc',
],
'include_dirs': [
'<(DEPTH)/third_party/wtl/include',
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 5d7e1d2..e4384ca 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -18,6 +18,7 @@
#include "base/singleton.h"
#include "base/stl_util-inl.h"
#include "base/third_party/nss/blapi.h"
+#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
@@ -30,6 +31,8 @@
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_l10n_util.h"
#include "chrome/common/extensions/extension_resource.h"
+#include "chrome/common/extensions/extension_sidebar_defaults.h"
+#include "chrome/common/extensions/extension_sidebar_utils.h"
#include "chrome/common/extensions/user_script.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/url_util.h"
@@ -194,6 +197,7 @@ const int Extension::kIconSizes[] = {
const int Extension::kPageActionIconMaxSize = 19;
const int Extension::kBrowserActionIconMaxSize = 19;
+const int Extension::kSidebarIconMaxSize = 16;
// Explicit permissions -- permission declaration required.
const char Extension::kBackgroundPermission[] = "background";
@@ -804,6 +808,51 @@ ExtensionAction* Extension::LoadExtensionActionHelper(
return result.release();
}
+ExtensionSidebarDefaults* Extension::LoadExtensionSidebarDefaults(
+ const DictionaryValue* extension_sidebar, std::string* error) {
+ scoped_ptr<ExtensionSidebarDefaults> result(new ExtensionSidebarDefaults());
+
+ std::string default_icon;
+ // Read sidebar's |default_icon| (optional).
+ if (extension_sidebar->HasKey(keys::kSidebarDefaultIcon)) {
+ if (!extension_sidebar->GetString(keys::kSidebarDefaultIcon,
+ &default_icon) ||
+ default_icon.empty()) {
+ *error = errors::kInvalidSidebarDefaultIconPath;
+ return NULL;
+ }
+ result->set_default_icon_path(default_icon);
+ }
+
+ // Read sidebar's |default_title| (optional).
+ string16 default_title;
+ if (extension_sidebar->HasKey(keys::kSidebarDefaultTitle)) {
+ if (!extension_sidebar->GetString(keys::kSidebarDefaultTitle,
+ &default_title)) {
+ *error = errors::kInvalidSidebarDefaultTitle;
+ return NULL;
+ }
+ }
+ result->set_default_title(default_title);
+
+ // Read sidebar's |default_url| (optional).
+ std::string default_url;
+ if (extension_sidebar->HasKey(keys::kSidebarDefaultUrl)) {
+ if (!extension_sidebar->GetString(keys::kSidebarDefaultUrl, &default_url) ||
+ default_url.empty()) {
+ *error = errors::kInvalidSidebarDefaultUrl;
+ return NULL;
+ }
+ GURL resolved_url = extension_sidebar_utils::ResolveAndVerifyUrl(
+ default_url, this, error);
+ if (!resolved_url.is_valid())
+ return NULL;
+ result->set_default_url(resolved_url);
+ }
+
+ return result.release();
+}
+
bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) const {
for (DictionaryValue::key_iterator key = source.begin_keys();
key != source.end_keys(); ++key) {
@@ -1927,6 +1976,23 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key,
InitEffectiveHostPermissions();
+ // Initialize sidebar action (optional). It has to be done after host
+ // permissions are initialized to verify default sidebar url.
+ if (source.HasKey(keys::kSidebar)) {
+ DictionaryValue* sidebar_value;
+ if (!source.GetDictionary(keys::kSidebar, &sidebar_value)) {
+ *error = errors::kInvalidSidebar;
+ return false;
+ }
+ if (!HasApiPermission(Extension::kExperimentalPermission)) {
+ *error = errors::kSidebarExperimental;
+ return false;
+ }
+ sidebar_defaults_.reset(LoadExtensionSidebarDefaults(sidebar_value, error));
+ if (!sidebar_defaults_.get())
+ return false; // Failed to parse sidebar definition.
+ }
+
// Although |source| is passed in as a const, it's still possible to modify
// it. This is dangerous since the utility process re-uses |source| after
// it calls InitFromValue, passing it up to the browser process which calls
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 4c82fd6..f58d273 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -26,6 +26,7 @@
class DictionaryValue;
class ExtensionAction;
class ExtensionResource;
+class ExtensionSidebarDefaults;
class SkBitmap;
class Version;
@@ -151,6 +152,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// Max size (both dimensions) for browser and page actions.
static const int kPageActionIconMaxSize;
static const int kBrowserActionIconMaxSize;
+ static const int kSidebarIconMaxSize;
// Each permission is a module that the extension is permitted to use.
//
@@ -414,6 +416,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
const UserScriptList& content_scripts() const { return content_scripts_; }
ExtensionAction* page_action() const { return page_action_.get(); }
ExtensionAction* browser_action() const { return browser_action_.get(); }
+ ExtensionSidebarDefaults* sidebar_defaults() const {
+ return sidebar_defaults_.get();
+ }
const std::vector<PluginInfo>& plugins() const { return plugins_; }
const GURL& background_url() const { return background_url_; }
const GURL& options_url() const { return options_url_; }
@@ -533,6 +538,11 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
ExtensionAction* LoadExtensionActionHelper(
const DictionaryValue* extension_action, std::string* error);
+ // Helper method to load an ExtensionSidebarDefaults from the sidebar manifest
+ // entry.
+ ExtensionSidebarDefaults* LoadExtensionSidebarDefaults(
+ const DictionaryValue* sidebar, std::string* error);
+
// Calculates the effective host permissions from the permissions and content
// script petterns.
void InitEffectiveHostPermissions();
@@ -626,6 +636,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// The extension's browser action, if any.
scoped_ptr<ExtensionAction> browser_action_;
+ // The extension's sidebar, if any.
+ scoped_ptr<ExtensionSidebarDefaults> sidebar_defaults_;
+
// Optional list of NPAPI plugins and associated properties.
std::vector<PluginInfo> plugins_;
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index dd15afa..af8bff3 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -52,6 +52,10 @@ const char* kPluginsPath = "path";
const char* kPluginsPublic = "public";
const char* kPublicKey = "key";
const char* kRunAt = "run_at";
+const char* kSidebar = "sidebar";
+const char* kSidebarDefaultIcon = "default_icon";
+const char* kSidebarDefaultTitle = "default_title";
+const char* kSidebarDefaultUrl = "default_url";
const char* kSignature = "signature";
const char* kTheme = "theme";
const char* kThemeColors = "colors";
@@ -225,6 +229,14 @@ const char* kInvalidPluginsPublic =
"Invalid value for 'plugins[*].public'.";
const char* kInvalidRunAt =
"Invalid value for 'content_scripts[*].run_at'.";
+extern const char* kInvalidSidebar =
+ "Invalid value for 'sidebar'.";
+extern const char* kInvalidSidebarDefaultIconPath =
+ "Invalid value for 'sidebar.default_icon'.";
+extern const char* kInvalidSidebarDefaultTitle =
+ "Invalid value for 'sidebar.default_title'.";
+extern const char* kInvalidSidebarDefaultUrl =
+ "Invalid value for 'sidebar.default_url'.";
const char* kInvalidSignature =
"Value 'signature' is missing or invalid.";
const char* kInvalidTheme =
@@ -288,6 +300,9 @@ const char* kOneUISurfaceOnly =
"Only one of 'browser_action', 'page_action', and 'app' can be specified.";
const char* kReservedMessageFound =
"Reserved key * found in message catalog.";
+const char* kSidebarExperimental =
+ "You must request the 'experimental' permission in order to use the"
+ " Sidebar API.";
const char* kThemesCannotContainExtensions =
"A theme cannot contain extensions code.";
#if defined(OS_CHROMEOS)
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index 3ae0729..d67966f 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -57,6 +57,10 @@ namespace extension_manifest_keys {
extern const char* kPluginsPublic;
extern const char* kPublicKey;
extern const char* kRunAt;
+ extern const char* kSidebar;
+ extern const char* kSidebarDefaultIcon;
+ extern const char* kSidebarDefaultTitle;
+ extern const char* kSidebarDefaultUrl;
extern const char* kSignature;
extern const char* kTheme;
extern const char* kThemeColors;
@@ -160,6 +164,10 @@ namespace extension_manifest_errors {
extern const char* kInvalidPluginsPath;
extern const char* kInvalidPluginsPublic;
extern const char* kInvalidRunAt;
+ extern const char* kInvalidSidebar;
+ extern const char* kInvalidSidebarDefaultIconPath;
+ extern const char* kInvalidSidebarDefaultTitle;
+ extern const char* kInvalidSidebarDefaultUrl;
extern const char* kInvalidSignature;
extern const char* kInvalidTheme;
extern const char* kInvalidThemeColors;
@@ -191,6 +199,7 @@ namespace extension_manifest_errors {
extern const char* kMultipleOverrides;
extern const char* kOneUISurfaceOnly;
extern const char* kReservedMessageFound;
+ extern const char* kSidebarExperimental;
extern const char* kThemesCannotContainExtensions;
extern const char* kWebContentMustBeEnabled;
#if defined(OS_CHROMEOS)
diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc
index f7c8f27..694cf39 100644
--- a/chrome/common/extensions/extension_manifests_unittest.cc
+++ b/chrome/common/extensions/extension_manifests_unittest.cc
@@ -8,11 +8,13 @@
#include "base/path_service.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.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_defaults.h"
#include "chrome/common/json_value_serializer.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -271,6 +273,47 @@ TEST_F(ExtensionManifestTest, DevToolsExtensions) {
*CommandLine::ForCurrentProcess() = old_command_line;
}
+TEST_F(ExtensionManifestTest, Sidebar) {
+ LoadAndExpectError("sidebar.json",
+ errors::kExperimentalFlagRequired);
+
+ CommandLine old_command_line = *CommandLine::ForCurrentProcess();
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalExtensionApis);
+
+ LoadAndExpectError("sidebar_no_permissions.json",
+ errors::kSidebarExperimental);
+
+ LoadAndExpectError("sidebar_icon_empty.json",
+ errors::kInvalidSidebarDefaultIconPath);
+ LoadAndExpectError("sidebar_icon_invalid_type.json",
+ errors::kInvalidSidebarDefaultIconPath);
+ LoadAndExpectError("sidebar_title_invalid_type.json",
+ errors::kInvalidSidebarDefaultTitle);
+ LoadAndExpectError("sidebar_url_invalid.json",
+ errors::kInvalidSidebarDefaultUrl);
+ LoadAndExpectError("sidebar_url_invalid_type.json",
+ errors::kInvalidSidebarDefaultUrl);
+ LoadAndExpectError("sidebar_url_no_permissions.json",
+ extension_manifest_errors::kCannotAccessPage);
+
+ scoped_refptr<Extension> extension(LoadAndExpectSuccess("sidebar.json"));
+ ASSERT_TRUE(extension->sidebar_defaults() != NULL);
+ EXPECT_EQ(extension->sidebar_defaults()->default_title(),
+ ASCIIToUTF16("Default title"));
+ EXPECT_EQ(extension->sidebar_defaults()->default_icon_path(),
+ "icon.png");
+ EXPECT_EQ(extension->url().spec() + "sidebar.html",
+ extension->sidebar_defaults()->default_url().spec());
+
+ scoped_refptr<Extension> extension_external_url(
+ LoadAndExpectSuccess("sidebar_external_url.json"));
+ EXPECT_EQ("http://sidebar.url/sidebar.html",
+ extension_external_url->sidebar_defaults()->default_url().spec());
+
+ *CommandLine::ForCurrentProcess() = old_command_line;
+}
+
TEST_F(ExtensionManifestTest, DisallowHybridApps) {
LoadAndExpectError("disallow_hybrid_1.json",
errors::kHostedAppsCannotIncludeExtensionFeatures);
diff --git a/chrome/common/extensions/extension_sidebar_defaults.h b/chrome/common/extensions/extension_sidebar_defaults.h
new file mode 100644
index 0000000..0b7fc6fa
--- /dev/null
+++ b/chrome/common/extensions/extension_sidebar_defaults.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_SIDEBAR_DEFAULTS_H_
+#define CHROME_COMMON_EXTENSIONS_EXTENSION_SIDEBAR_DEFAULTS_H_
+#pragma once
+
+#include <string>
+
+#include "base/string16.h"
+#include "googleurl/src/gurl.h"
+
+class SkBitmap;
+
+// ExtensionSidebarDefaults encapsulates the default parameters of a sidebar,
+// as defined in the extension manifest.
+class ExtensionSidebarDefaults {
+ public:
+ // Default title, stores manifest default_title key value.
+ void set_default_title(const string16& title) {
+ default_title_ = title;
+ }
+ const string16& default_title() const { return default_title_; }
+
+ // Default icon path, stores manifest default_icon key value.
+ void set_default_icon_path(const std::string& path) {
+ default_icon_path_ = path;
+ }
+ const std::string& default_icon_path() const {
+ return default_icon_path_;
+ }
+
+ // Default sidebar url, resolved and verified against extension permissions.
+ void set_default_url(const GURL& url) {
+ default_url_ = url;
+ }
+ const GURL& default_url() const {
+ return default_url_;
+ }
+
+ private:
+ string16 default_title_;
+ std::string default_icon_path_;
+ GURL default_url_;
+};
+
+#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_SIDEBAR_DEFAULTS_H_
diff --git a/chrome/common/extensions/extension_sidebar_utils.cc b/chrome/common/extensions/extension_sidebar_utils.cc
new file mode 100644
index 0000000..d2abfa9
--- /dev/null
+++ b/chrome/common/extensions/extension_sidebar_utils.cc
@@ -0,0 +1,64 @@
+// 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/common/extensions/extension_sidebar_utils.h"
+
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_error_utils.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+
+namespace {
+
+// Errors.
+const char kInvalidUrlError[] = "Invalid url: \"*\".";
+
+bool CanUseHost(const Extension* extension,
+ const GURL& url,
+ std::string* error) {
+ if (extension->HasHostPermission(url))
+ return true;
+
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ extension_manifest_errors::kCannotAccessPage, url.spec());
+ return false;
+}
+
+} // namespace
+
+namespace extension_sidebar_utils {
+
+std::string GetExtensionIdByContentId(const std::string& content_id) {
+ // At the moment, content_id == extension_id.
+ return content_id;
+}
+
+GURL ResolveAndVerifyUrl(const std::string& url_string,
+ const Extension* extension,
+ std::string* error) {
+ // Resolve possibly relative URL.
+ GURL url(url_string);
+ if (!url.is_valid())
+ url = extension->GetResourceURL(url_string);
+
+ if (!url.is_valid()) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidUrlError,
+ url_string);
+ return GURL();
+ }
+ if (!url.SchemeIs(chrome::kExtensionScheme) &&
+ !CanUseHost(extension, url, error)) {
+ return GURL();
+ }
+ // Disallow requests outside of the requesting extension view's extension.
+ if (url.SchemeIs(chrome::kExtensionScheme)) {
+ std::string extension_id(url.host());
+ if (extension_id != extension->id())
+ return GURL();
+ }
+
+ return url;
+}
+
+} // namespace extension_sidebar_utils
diff --git a/chrome/common/extensions/extension_sidebar_utils.h b/chrome/common/extensions/extension_sidebar_utils.h
new file mode 100644
index 0000000..08a0ef4
--- /dev/null
+++ b/chrome/common/extensions/extension_sidebar_utils.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_SIDEBAR_UTILS_H_
+#define CHROME_COMMON_EXTENSIONS_EXTENSION_SIDEBAR_UTILS_H_
+#pragma once
+
+#include <string>
+
+class Extension;
+class GURL;
+
+namespace extension_sidebar_utils {
+
+// Returns id of an extension owning a sidebar identified by |content_id|.
+std::string GetExtensionIdByContentId(const std::string& content_id);
+
+// Resolves |url_string| relative to |extension|'s url and verifies it
+// against |extension|'s host permissions.
+// In case of any problem, returns an empty invalid GURL and |error| receives
+// the corresponding error message.
+GURL ResolveAndVerifyUrl(const std::string& url_string,
+ const Extension* extension,
+ std::string* error);
+
+} // namespace extension_sidebar_utils
+
+#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_SIDEBAR_UTILS_H_
diff --git a/chrome/test/data/extensions/api_test/sidebar/manifest.json b/chrome/test/data/extensions/api_test/sidebar/manifest.json
index 4a55643..dbbce99 100644
--- a/chrome/test/data/extensions/api_test/sidebar/manifest.json
+++ b/chrome/test/data/extensions/api_test/sidebar/manifest.json
@@ -3,5 +3,6 @@
"version": "0.1",
"description": "end-to-end browser test for chrome.experimental.sidebar API",
"background_page": "test.html",
+ "sidebar": {},
"permissions": ["tabs", "experimental"]
}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar.json b/chrome/test/data/extensions/manifest_tests/sidebar.json
new file mode 100644
index 0000000..a1ed809
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar.json
@@ -0,0 +1,12 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_url": "sidebar.html",
+ "default_title": "Default title",
+ "default_icon": "icon.png"
+ },
+ "permissions": [
+ "experimental"
+ ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_external_url.json b/chrome/test/data/extensions/manifest_tests/sidebar_external_url.json
new file mode 100644
index 0000000..c6e1cdb
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_external_url.json
@@ -0,0 +1,10 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_url": "http://sidebar.url/sidebar.html"
+ },
+ "permissions": [
+ "experimental", "http://sidebar.url/"
+ ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_icon_empty.json b/chrome/test/data/extensions/manifest_tests/sidebar_icon_empty.json
new file mode 100644
index 0000000..4606f38
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_icon_empty.json
@@ -0,0 +1,11 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_title": "Default title",
+ "default_icon": ""
+ },
+ "permissions": [
+ "experimental"
+ ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_icon_invalid_type.json b/chrome/test/data/extensions/manifest_tests/sidebar_icon_invalid_type.json
new file mode 100644
index 0000000..e88d09c
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_icon_invalid_type.json
@@ -0,0 +1,11 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_title": "Default title",
+ "default_icon": 42
+ },
+ "permissions": [
+ "experimental"
+ ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_no_permissions.json b/chrome/test/data/extensions/manifest_tests/sidebar_no_permissions.json
new file mode 100644
index 0000000..f189352
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_no_permissions.json
@@ -0,0 +1,9 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_title": "Default title",
+ "default_icon": "icon.png"
+ },
+ "permissions": []
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_title_invalid_type.json b/chrome/test/data/extensions/manifest_tests/sidebar_title_invalid_type.json
new file mode 100644
index 0000000..a16f342
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_title_invalid_type.json
@@ -0,0 +1,11 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_title": 42,
+ "default_icon": "icon.png"
+ },
+ "permissions": [
+ "experimental"
+ ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_url_invalid.json b/chrome/test/data/extensions/manifest_tests/sidebar_url_invalid.json
new file mode 100644
index 0000000..be023db
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_url_invalid.json
@@ -0,0 +1,12 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_url": "",
+ "default_title": "Default title",
+ "default_icon": "icon.png"
+ },
+ "permissions": [
+ "experimental"
+ ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_url_invalid_type.json b/chrome/test/data/extensions/manifest_tests/sidebar_url_invalid_type.json
new file mode 100644
index 0000000..d212c90
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_url_invalid_type.json
@@ -0,0 +1,12 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_url": 42,
+ "default_title": "Default title",
+ "default_icon": "icon.png"
+ },
+ "permissions": [
+ "experimental"
+ ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/sidebar_url_no_permissions.json b/chrome/test/data/extensions/manifest_tests/sidebar_url_no_permissions.json
new file mode 100644
index 0000000..b4c145f
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/sidebar_url_no_permissions.json
@@ -0,0 +1,12 @@
+{
+ "name": "test",
+ "version": "1",
+ "sidebar": {
+ "default_url": "http://default.url",
+ "default_title": "Default title",
+ "default_icon": "icon.png"
+ },
+ "permissions": [
+ "experimental"
+ ]
+}
diff --git a/chrome/test/data/sidebar/manifest.json b/chrome/test/data/sidebar/manifest.json
new file mode 100644
index 0000000..2031ef6
--- /dev/null
+++ b/chrome/test/data/sidebar/manifest.json
@@ -0,0 +1,7 @@
+{
+ "name": "SidebarTest",
+ "version": "0.1",
+ "description": "interactive_ui_tests, SidebarTest.*",
+ "sidebar": {},
+ "permissions": ["experimental"]
+}