Commit 867f72e5 authored by wittman@chromium.org's avatar wittman@chromium.org

Copy ui_overrides files from settings_overrides to preserve history

This change is in support of later changes moving the bookmarks_ui
manifest key from chrome_settings_overrides to chrome_ui_overrides.

BUG=349049
R=kalman@chromium.org

Review URL: https://codereview.chromium.org/188813004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@255660 0039d316-1c4b-4281-b951-d872f2087c98
parent 33940aae
<h1>Settings Overrides</h1>
<p>
Settings overrides are a way for extensions to override selected Chrome settings
and user interface properties.
</p>
<h2 id="bookmarks">Bookmarks User Interface</h2>
<p>
Register the settings you want to override in the
<a href="manifest.html">extension manifest</a> like this:
</p>
<pre>{
"name": "My extension",
...
<b>
"chrome_settings_overrides" : {
"bookmarks_ui": {
"remove_button": "true",
"remove_bookmark_shortcut": "true"
}
}</b>,
...
}</pre>
<p>
<ul>
<li>
<a href="#bookmarks_ui">Bookmark button</a>: the "star" button that is used
to bookmark pages. Extensions may remove this button using the settings
overrides, and optionally replace it with a browser action or page action.
</li>
<li>
<a href="#bookmarks_ui">Bookmark shortcut</a>: the shortcut key that is
used to bookmark a page (Ctrl-D on Windows). Extensions may remove this
shortcut via the settings overrides, and optionally bind their own command
to it using the <code>commands</code> section of the manifest.
</li>
</ul>
</p>
<p class="note">
<b>Note:</b> Settings overrides for <code>bookmarks_ui</code> are only enabled
in the Chrome Dev release, and Chrome must be started with the
<code>--enable-override-bookmarks-ui=1</code> command line flag to enable
bookmarks user interface overrides.</p>
<h2 id="others">Homepage, Search Provider, and Startup Pages</h2>
<p>
Here is an example how <a href="#homepage">homepage</a>, <a
href="#search_provider">search provider</a> and <a href="#startup_pages">startup
pages</a> can be modified in the <a href="manifest.html">extension
manifest</a>.</p>
<pre>{
"name": "My extension",
...
"chrome_settings_overrides": {
"homepage": "http://www.homepage.com",
"search_provider": {
"name": "name.__MSG_url_domain__",
"keyword": "keyword.__MSG_url_domain__",
"search_url": "http://www.foo.__MSG_url_domain__/s?q={searchTerms}",
"favicon_url": "http://www.foo.__MSG_url_domain__/favicon.ico",
"suggest_url": "http://www.foo.__MSG_url_domain__/suggest?q={searchTerms}",
"instant_url": "http://www.foo.__MSG_url_domain__/instant?q={searchTerms}",
"image_url": "http://www.foo.__MSG_url_domain__/image?q={searchTerms}",
"search_url_post_params": "search_lang=__MSG_url_domain__",
"suggest_url_post_params": "suggest_lang=__MSG_url_domain__",
"instant_url_post_params": "instant_lang=__MSG_url_domain__",
"image_url_post_params": "image_lang=__MSG_url_domain__",
"alternate_urls": [
"http://www.moo.__MSG_url_domain__/s?q={searchTerms}",
"http://www.noo.__MSG_url_domain__/s?q={searchTerms}"
],
"encoding": "UTF-8",
"is_default": true
},
"startup_pages": ["http://www.startup.com"]
},
"default_locale": "de",
...
}</pre>
<p class="note">
<b>Note:</b> Settings overrides for <code>homepage</code>,
<code>search_provider</code>, and <code>startup_pages</code> are only enabled
in the Chrome Dev release.</p>
<h2 id="reference">Reference</h2>
<p>
An extension can override one or more of the following properties in the
manifest:
</p>
<ul>
{{#f:apis.manifestTypes.byName.ChromeSettingsOverrides.properties}}
<li>
<p id="{{f.name}}"><b><code>{{f.name}}</code> ({{+partials.variable_type type:f/}})</b></p>
<p>{{f.description}}</p>
{{?f.properties}}
<table>
<tr><th>Type</th><th>Attribute</th><th>Description</th></tr>
{{+partials.type_item
title:f.strings.properties
display_name:display_name
items:f.properties
item_partial:(partials.parameter_full hideParamTOC:true)/}}
</table>
{{/}}
{{/}}
</ul>
// Copyright 2013 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/manifest_handlers/ui_overrides_handler.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/permissions/settings_override_permission.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/permissions/manifest_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/permissions_info.h"
#include "grit/generated_resources.h"
#include "ipc/ipc_message.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
using extensions::api::manifest_types::ChromeSettingsOverrides;
namespace extensions {
namespace {
const char* kWwwPrefix = "www.";
scoped_ptr<GURL> CreateManifestURL(const std::string& url) {
scoped_ptr<GURL> manifest_url(new GURL(url));
if (!manifest_url->is_valid() ||
!manifest_url->SchemeIsHTTPOrHTTPS())
return scoped_ptr<GURL>();
return manifest_url.Pass();
}
scoped_ptr<GURL> ParseHomepage(const ChromeSettingsOverrides& overrides,
base::string16* error) {
if (!overrides.homepage)
return scoped_ptr<GURL>();
scoped_ptr<GURL> manifest_url = CreateManifestURL(*overrides.homepage);
if (!manifest_url) {
*error = extensions::ErrorUtils::FormatErrorMessageUTF16(
manifest_errors::kInvalidHomepageOverrideURL, *overrides.homepage);
}
return manifest_url.Pass();
}
std::vector<GURL> ParseStartupPage(const ChromeSettingsOverrides& overrides,
base::string16* error) {
std::vector<GURL> urls;
if (!overrides.startup_pages)
return urls;
for (std::vector<std::string>::const_iterator i =
overrides.startup_pages->begin(); i != overrides.startup_pages->end();
++i) {
scoped_ptr<GURL> manifest_url = CreateManifestURL(*i);
if (!manifest_url) {
*error = extensions::ErrorUtils::FormatErrorMessageUTF16(
manifest_errors::kInvalidStartupOverrideURL, *i);
} else {
urls.push_back(GURL());
urls.back().Swap(manifest_url.get());
}
}
return urls;
}
scoped_ptr<ChromeSettingsOverrides::Search_provider> ParseSearchEngine(
ChromeSettingsOverrides* overrides,
base::string16* error) {
if (!overrides->search_provider)
return scoped_ptr<ChromeSettingsOverrides::Search_provider>();
if (!CreateManifestURL(overrides->search_provider->favicon_url)) {
*error = extensions::ErrorUtils::FormatErrorMessageUTF16(
manifest_errors::kInvalidSearchEngineURL,
overrides->search_provider->favicon_url);
return scoped_ptr<ChromeSettingsOverrides::Search_provider>();
}
if (!CreateManifestURL(overrides->search_provider->search_url)) {
*error = extensions::ErrorUtils::FormatErrorMessageUTF16(
manifest_errors::kInvalidSearchEngineURL,
overrides->search_provider->search_url);
return scoped_ptr<ChromeSettingsOverrides::Search_provider>();
}
return overrides->search_provider.Pass();
}
// A www. prefix is not informative and thus not worth the limited real estate
// in the permissions UI.
std::string RemoveWwwPrefix(const std::string& url) {
if (StartsWithASCII(url, kWwwPrefix, false))
return url.substr(strlen(kWwwPrefix));
return url;
}
} // namespace
// The manifest permission implementation supports a permission for overriding
// the bookmark UI.
class SettingsOverridesHandler::ManifestPermissionImpl
: public ManifestPermission {
public:
explicit ManifestPermissionImpl(bool override_bookmarks_ui_permission)
: override_bookmarks_ui_permission_(override_bookmarks_ui_permission) {}
// extensions::ManifestPermission overrides.
virtual std::string name() const OVERRIDE {
return manifest_keys::kSettingsOverride;
}
virtual std::string id() const OVERRIDE {
return name();
}
virtual bool HasMessages() const OVERRIDE {
return override_bookmarks_ui_permission_;
}
virtual PermissionMessages GetMessages() const OVERRIDE {
PermissionMessages result;
if (override_bookmarks_ui_permission_) {
result.push_back(PermissionMessage(
PermissionMessage::kOverrideBookmarksUI,
l10n_util::GetStringUTF16(
IDS_EXTENSION_PROMPT_WARNING_OVERRIDE_BOOKMARKS_UI)));
}
return result;
}
virtual bool FromValue(const base::Value* value) OVERRIDE {
return value && value->GetAsBoolean(&override_bookmarks_ui_permission_);
}
virtual scoped_ptr<base::Value> ToValue() const OVERRIDE {
return scoped_ptr<base::Value>(
new base::FundamentalValue(override_bookmarks_ui_permission_)).Pass();
}
virtual ManifestPermission* Clone() const OVERRIDE {
return scoped_ptr<ManifestPermissionImpl>(
new ManifestPermissionImpl(
override_bookmarks_ui_permission_)).release();
}
virtual ManifestPermission* Diff(const ManifestPermission* rhs) const
OVERRIDE {
const ManifestPermissionImpl* other =
static_cast<const ManifestPermissionImpl*>(rhs);
return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
override_bookmarks_ui_permission_ &&
!other->override_bookmarks_ui_permission_)).release();
}
virtual ManifestPermission* Union(const ManifestPermission* rhs) const
OVERRIDE {
const ManifestPermissionImpl* other =
static_cast<const ManifestPermissionImpl*>(rhs);
return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
override_bookmarks_ui_permission_ ||
other->override_bookmarks_ui_permission_)).release();
}
virtual ManifestPermission* Intersect(const ManifestPermission* rhs) const
OVERRIDE {
const ManifestPermissionImpl* other =
static_cast<const ManifestPermissionImpl*>(rhs);
return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
override_bookmarks_ui_permission_ &&
other->override_bookmarks_ui_permission_)).release();
}
virtual bool Contains(const ManifestPermission* rhs) const OVERRIDE {
const ManifestPermissionImpl* other =
static_cast<const ManifestPermissionImpl*>(rhs);
return !other->override_bookmarks_ui_permission_ ||
override_bookmarks_ui_permission_;
}
virtual bool Equal(const ManifestPermission* rhs) const OVERRIDE {
const ManifestPermissionImpl* other =
static_cast<const ManifestPermissionImpl*>(rhs);
return override_bookmarks_ui_permission_ ==
other->override_bookmarks_ui_permission_;
}
virtual void Write(IPC::Message* m) const OVERRIDE {
IPC::WriteParam(m, override_bookmarks_ui_permission_);
}
virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE {
return IPC::ReadParam(m, iter, &override_bookmarks_ui_permission_);
}
virtual void Log(std::string* log) const OVERRIDE {
IPC::LogParam(override_bookmarks_ui_permission_, log);
}
private:
bool override_bookmarks_ui_permission_;
};
SettingsOverrides::SettingsOverrides() {}
SettingsOverrides::~SettingsOverrides() {}
const SettingsOverrides* SettingsOverrides::Get(
const Extension* extension) {
return static_cast<SettingsOverrides*>(
extension->GetManifestData(manifest_keys::kSettingsOverride));
}
bool SettingsOverrides::RemovesBookmarkButton(
const SettingsOverrides& settings_overrides) {
return settings_overrides.bookmarks_ui &&
settings_overrides.bookmarks_ui->remove_button &&
*settings_overrides.bookmarks_ui->remove_button;
}
bool SettingsOverrides::RemovesBookmarkShortcut(
const SettingsOverrides& settings_overrides) {
return settings_overrides.bookmarks_ui &&
settings_overrides.bookmarks_ui->remove_bookmark_shortcut &&
*settings_overrides.bookmarks_ui->remove_bookmark_shortcut;
}
SettingsOverridesHandler::SettingsOverridesHandler() {}
SettingsOverridesHandler::~SettingsOverridesHandler() {}
bool SettingsOverridesHandler::Parse(Extension* extension,
base::string16* error) {
const base::Value* dict = NULL;
CHECK(extension->manifest()->Get(manifest_keys::kSettingsOverride, &dict));
scoped_ptr<ChromeSettingsOverrides> settings(
ChromeSettingsOverrides::FromValue(*dict, error));
if (!settings)
return false;
scoped_ptr<SettingsOverrides> info(new SettingsOverrides);
info->bookmarks_ui.swap(settings->bookmarks_ui);
// Support backward compatibility for deprecated key
// chrome_settings_overrides.bookmarks_ui.hide_bookmark_button.
if (info->bookmarks_ui && !info->bookmarks_ui->remove_button &&
info->bookmarks_ui->hide_bookmark_button) {
info->bookmarks_ui->remove_button.reset(
new bool(*info->bookmarks_ui->hide_bookmark_button));
}
info->homepage = ParseHomepage(*settings, error);
info->search_engine = ParseSearchEngine(settings.get(), error);
info->startup_pages = ParseStartupPage(*settings, error);
if (!info->bookmarks_ui && !info->homepage &&
!info->search_engine && info->startup_pages.empty()) {
*error =
base::ASCIIToUTF16(manifest_errors::kInvalidEmptySettingsOverrides);
return false;
}
info->manifest_permission.reset(new ManifestPermissionImpl(
SettingsOverrides::RemovesBookmarkButton(*info)));
APIPermissionSet* permission_set =
PermissionsData::GetInitialAPIPermissions(extension);
DCHECK(permission_set);
if (info->search_engine) {
permission_set->insert(new SettingsOverrideAPIPermission(
PermissionsInfo::GetInstance()->GetByID(APIPermission::kSearchProvider),
RemoveWwwPrefix(CreateManifestURL(info->search_engine->search_url)->
GetOrigin().host())));
}
if (!info->startup_pages.empty()) {
permission_set->insert(new SettingsOverrideAPIPermission(
PermissionsInfo::GetInstance()->GetByID(APIPermission::kStartupPages),
// We only support one startup page even though the type of the manifest
// property is a list, only the first one is used.
RemoveWwwPrefix(info->startup_pages[0].GetContent())));
}
if (info->homepage) {
permission_set->insert(new SettingsOverrideAPIPermission(
PermissionsInfo::GetInstance()->GetByID(APIPermission::kHomepage),
RemoveWwwPrefix(info->homepage.get()->GetContent())));
}
extension->SetManifestData(manifest_keys::kSettingsOverride,
info.release());
return true;
}
bool SettingsOverridesHandler::Validate(
const Extension* extension,
std::string* error,
std::vector<InstallWarning>* warnings) const {
const SettingsOverrides* settings_overrides =
SettingsOverrides::Get(extension);
if (settings_overrides && settings_overrides->bookmarks_ui) {
if (!FeatureSwitch::enable_override_bookmarks_ui()->IsEnabled()) {
warnings->push_back(InstallWarning(
ErrorUtils::FormatErrorMessage(
manifest_errors::kUnrecognizedManifestKey,
manifest_keys::kBookmarkUI)));
} else if (settings_overrides->bookmarks_ui->hide_bookmark_button) {
warnings->push_back(InstallWarning(
ErrorUtils::FormatErrorMessage(
manifest_errors::kKeyIsDeprecatedWithReplacement,
manifest_keys::kHideBookmarkButton,
manifest_keys::kRemoveButton)));
}
}
return true;
}
ManifestPermission* SettingsOverridesHandler::CreatePermission() {
return new ManifestPermissionImpl(false);
}
ManifestPermission* SettingsOverridesHandler::CreateInitialRequiredPermission(
const Extension* extension) {
const SettingsOverrides* data = SettingsOverrides::Get(extension);
if (data)
return data->manifest_permission->Clone();
return NULL;
}
const std::vector<std::string> SettingsOverridesHandler::Keys() const {
return SingleKey(manifest_keys::kSettingsOverride);
}
} // namespace extensions
// Copyright 2013 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_MANIFEST_HANDLERS_UI_OVERRIDES_HANDLER_H_
#define CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_UI_OVERRIDES_HANDLER_H_
#include "chrome/common/extensions/api/manifest_types.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handler.h"
namespace extensions {
class ManifestPermission;
// SettingsOverride is associated with "chrome_settings_overrides" manifest key.
// An extension can add a search engine as default or non-default, overwrite the
// homepage and append a startup page to the list.
struct SettingsOverrides : public Extension::ManifestData {
SettingsOverrides();
virtual ~SettingsOverrides();
static const SettingsOverrides* Get(const Extension* extension);
static bool RemovesBookmarkButton(
const SettingsOverrides& settings_overrides);
static bool RemovesBookmarkShortcut(
const SettingsOverrides& settings_overrides);
scoped_ptr<api::manifest_types::ChromeSettingsOverrides::Bookmarks_ui>
bookmarks_ui;
scoped_ptr<api::manifest_types::ChromeSettingsOverrides::Search_provider>
search_engine;
scoped_ptr<GURL> homepage;
std::vector<GURL> startup_pages;
scoped_ptr<ManifestPermission> manifest_permission;
private:
DISALLOW_COPY_AND_ASSIGN(SettingsOverrides);
};
class SettingsOverridesHandler : public ManifestHandler {
public:
SettingsOverridesHandler();
virtual ~SettingsOverridesHandler();
virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE;
virtual bool Validate(const Extension* extension,
std::string* error,
std::vector<InstallWarning>* warnings) const OVERRIDE;
virtual ManifestPermission* CreatePermission() OVERRIDE;
virtual ManifestPermission* CreateInitialRequiredPermission(
const Extension* extension) OVERRIDE;
private:
class ManifestPermissionImpl;
virtual const std::vector<std::string> Keys() const OVERRIDE;
DISALLOW_COPY_AND_ASSIGN(SettingsOverridesHandler);
};
} // namespace extensions
#endif // CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_UI_OVERRIDES_HANDLER_H_
// Copyright 2013 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/manifest_handlers/ui_overrides_handler.h"
#include "base/json/json_string_value_serializer.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/extensions/features/feature_channel.h"
#include "chrome/common/extensions/manifest_url_handler.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kManifest[] = "{"
" \"version\" : \"1.0.0.0\","
" \"name\" : \"Test\","
" \"chrome_settings_overrides\" : {"
" \"homepage\" : \"http://www.homepage.com\","
" \"search_provider\" : {"
" \"name\" : \"first\","
" \"keyword\" : \"firstkey\","
" \"search_url\" : \"http://www.foo.com/s?q={searchTerms}\","
" \"favicon_url\" : \"http://www.foo.com/favicon.ico\","
" \"suggest_url\" : \"http://www.foo.com/s?q={searchTerms}\","
" \"encoding\" : \"UTF-8\","
" \"is_default\" : true"
" },"
" \"startup_pages\" : [\"http://www.startup.com\"]"
" }"
"}";
const char kBrokenManifest[] = "{"
" \"version\" : \"1.0.0.0\","
" \"name\" : \"Test\","
" \"chrome_settings_overrides\" : {"
" \"homepage\" : \"{invalid}\","
" \"search_provider\" : {"
" \"name\" : \"first\","
" \"keyword\" : \"firstkey\","
" \"search_url\" : \"{invalid}/s?q={searchTerms}\","
" \"favicon_url\" : \"{invalid}/favicon.ico\","
" \"encoding\" : \"UTF-8\","
" \"is_default\" : true"
" },"
" \"startup_pages\" : [\"{invalid}\"]"
" }"
"}";
using extensions::api::manifest_types::ChromeSettingsOverrides;
using extensions::Extension;
using extensions::Manifest;
using extensions::SettingsOverrides;
namespace manifest_keys = extensions::manifest_keys;
class OverrideSettingsTest : public testing::Test {
};
TEST_F(OverrideSettingsTest, ParseManifest) {
extensions::ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_DEV);
std::string manifest(kManifest);
JSONStringValueSerializer json(&manifest);
std::string error;
scoped_ptr<base::Value> root(json.Deserialize(NULL, &error));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
scoped_refptr<Extension> extension = Extension::Create(
base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
Manifest::INVALID_LOCATION,
*static_cast<base::DictionaryValue*>(root.get()),
Extension::NO_FLAGS,
&error);
ASSERT_TRUE(extension);
#if defined(OS_WIN)
ASSERT_TRUE(extension->manifest()->HasPath(manifest_keys::kSettingsOverride));
SettingsOverrides* settings_override = static_cast<SettingsOverrides*>(
extension->GetManifestData(manifest_keys::kSettingsOverride));
ASSERT_TRUE(settings_override);
ASSERT_TRUE(settings_override->search_engine);
EXPECT_TRUE(settings_override->search_engine->is_default);
const ChromeSettingsOverrides::Search_provider* search_engine =
settings_override->search_engine.get();
EXPECT_EQ("first", search_engine->name);
EXPECT_EQ("firstkey", search_engine->keyword);
EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", search_engine->search_url);
EXPECT_EQ("http://www.foo.com/favicon.ico", search_engine->favicon_url);
EXPECT_EQ("http://www.foo.com/s?q={searchTerms}",
*search_engine->suggest_url);
EXPECT_EQ("UTF-8", search_engine->encoding);
EXPECT_EQ(std::vector<GURL>(1, GURL("http://www.startup.com")),
settings_override->startup_pages);
ASSERT_TRUE(settings_override->homepage);
EXPECT_EQ(GURL("http://www.homepage.com"), *settings_override->homepage);
#else
EXPECT_FALSE(
extension->manifest()->HasPath(manifest_keys::kSettingsOverride));
#endif
}
TEST_F(OverrideSettingsTest, ParseBrokenManifest) {
extensions::ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_DEV);
std::string manifest(kBrokenManifest);
JSONStringValueSerializer json(&manifest);
std::string error;
scoped_ptr<base::Value> root(json.Deserialize(NULL, &error));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
scoped_refptr<Extension> extension = Extension::Create(
base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
Manifest::INVALID_LOCATION,
*static_cast<base::DictionaryValue*>(root.get()),
Extension::NO_FLAGS,
&error);
#if defined(OS_WIN)
EXPECT_FALSE(extension);
EXPECT_EQ(
std::string(extensions::manifest_errors::kInvalidEmptySettingsOverrides),
error);
#else
EXPECT_TRUE(extension);
EXPECT_FALSE(
extension->manifest()->HasPath(manifest_keys::kSettingsOverride));
#endif
}
} // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment