// Copyright (c) 2011 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_preference_api.h"
#include <map>
#include "base/json/json_writer.h"
#include "base/memory/singleton.h"
#include "base/stl_util-inl.h"
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_proxy_api.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "content/common/notification_type.h"
#include "content/common/notification_service.h"
namespace {
struct PrefMappingEntry {
const char* extension_pref;
const char* browser_pref;
const char* permission;
};
const char kNotControllable[] = "NotControllable";
const char kControlledByOtherExtensions[] = "ControlledByOtherExtensions";
const char kControllableByThisExtension[] = "ControllableByThisExtension";
const char kControlledByThisExtension[] = "ControlledByThisExtension";
const char kIncognito[] = "incognito";
const char kIncognitoSpecific[] = "incognitoSpecific";
const char kLevelOfControl[] = "levelOfControl";
const char kValue[] = "value";
const char kOnPrefChangeFormat[] = "experimental.preferences.%s.onChange";
const char kIncognitoErrorMessage[] =
"You do not have permission to access incognito preferences.";
const char kPermissionErrorMessage[] =
"You do not have permission to access the preference '%s'. "
"Be sure to declare in your manifest what permissions you need.";
PrefMappingEntry kPrefMapping[] = {
{ "blockThirdPartyCookies",
prefs::kBlockThirdPartyCookies,
Extension::kContentSettingsPermission
},
{ "enableReferrers",
prefs::kEnableReferrers,
Extension::kContentSettingsPermission
},
{ "enableHyperlinkAuditing",
prefs::kEnableHyperlinkAuditing,
Extension::kContentSettingsPermission
},
{ "proxy",
prefs::kProxy,
Extension::kProxyPermission
},
};
class IdentityPrefTransformer : public PrefTransformerInterface {
public:
IdentityPrefTransformer() { }
virtual ~IdentityPrefTransformer() { }
virtual Value* ExtensionToBrowserPref(const Value* extension_pref,
std::string* error) {
return extension_pref->DeepCopy();
}
virtual Value* BrowserToExtensionPref(const Value* browser_pref) {
return browser_pref->DeepCopy();
}
};
// Returns a string constant (defined in the API) indicating the level of
// control this extension has over the specified preference.
const char* GetLevelOfControl(
Profile* profile,
const std::string& extension_id,
const std::string& browser_pref,
bool incognito) {
PrefService* prefs = incognito ? profile->GetOffTheRecordPrefs()
: profile->GetPrefs();
const PrefService::Preference* pref =
prefs->FindPreference(browser_pref.c_str());
CHECK(pref);
ExtensionPrefs* ep = profile->GetExtensionService()->extension_prefs();
if (!pref->IsExtensionModifiable())
return kNotControllable;
if (ep->DoesExtensionControlPref(extension_id, browser_pref, incognito))
return kControlledByThisExtension;
if (ep->CanExtensionControlPref(extension_id, browser_pref, incognito))
return kControllableByThisExtension;
return kControlledByOtherExtensions;
}
class PrefMapping {
public:
static PrefMapping* GetInstance() {
return Singleton<PrefMapping>::get();
}
bool FindBrowserPrefForExtensionPref(const std::string& extension_pref,
std::string* browser_pref,
std::string* permission) {
std::map<std::string, std::pair<std::string, std::string> >::iterator it =
mapping_.find(extension_pref);
if (it != mapping_.end()) {
*browser_pref = it->second.first;
*permission = it->second.second;
return true;
}
return false;
}
bool FindEventForBrowserPref(const std::string& browser_pref,
std::string* event_name,
std::string* permission) {
std::map<std::string, std::pair<std::string, std::string> >::iterator it =
event_mapping_.find(browser_pref);
if (it != event_mapping_.end()) {
*event_name = it->second.first;
*permission = it->second.second;
return true;
}
return false;
}
PrefTransformerInterface* FindTransformerForBrowserPref(
const std::string& browser_pref) {
std::map<std::string, PrefTransformerInterface*>::iterator it =
transformers_.find(browser_pref);
if (it != transformers_.end())
return it->second;
else
return identity_transformer_.get();
}
private:
friend struct DefaultSingletonTraits<PrefMapping>;
PrefMapping() {
identity_transformer_.reset(new IdentityPrefTransformer());
for (size_t i = 0; i < arraysize(kPrefMapping); ++i) {
mapping_[kPrefMapping[i].extension_pref] =
std::make_pair(kPrefMapping[i].browser_pref,
kPrefMapping[i].permission);
std::string event_name =
base::StringPrintf(kOnPrefChangeFormat,
kPrefMapping[i].extension_pref);
event_mapping_[kPrefMapping[i].browser_pref] =
std::make_pair(event_name, kPrefMapping[i].permission);
}
DCHECK_EQ(arraysize(kPrefMapping), mapping_.size());
DCHECK_EQ(arraysize(kPrefMapping), event_mapping_.size());
RegisterPrefTransformer(prefs::kProxy, new ProxyPrefTransformer());
}
~PrefMapping() {
STLDeleteContainerPairSecondPointers(transformers_.begin(),
transformers_.end());
}
void RegisterPrefTransformer(const std::string& browser_pref,
PrefTransformerInterface* transformer) {
DCHECK_EQ(0u, transformers_.count(browser_pref)) <<
"Trying to register pref transformer for " << browser_pref << " twice";
transformers_[browser_pref] = transformer;
}
// Mapping from extension pref keys to browser pref keys and permissions.
std::map<std::string, std::pair<std::string, std::string> > mapping_;
// Mapping from browser pref keys to extension event names and permissions.
std::map<std::string, std::pair<std::string, std::string> > event_mapping_;
// Mapping from browser pref keys to transformers.
std::map<std::string, PrefTransformerInterface*> transformers_;
scoped_ptr<PrefTransformerInterface> identity_transformer_;
DISALLOW_COPY_AND_ASSIGN(PrefMapping);
};
} // namespace
ExtensionPreferenceEventRouter::ExtensionPreferenceEventRouter(
Profile* profile) : profile_(profile) {
registrar_.Init(profile_->GetPrefs());
incognito_registrar_.Init(profile_->GetOffTheRecordPrefs());
for (size_t i = 0; i < arraysize(kPrefMapping); ++i) {
registrar_.Add(kPrefMapping[i].browser_pref, this);
incognito_registrar_.Add(kPrefMapping[i].browser_pref, this);
}
}
ExtensionPreferenceEventRouter::~ExtensionPreferenceEventRouter() { }
void ExtensionPreferenceEventRouter::Observe(
NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
if (type == NotificationType::PREF_CHANGED) {
const std::string* pref_key =
Details<const std::string>(details).ptr();
OnPrefChanged(Source<PrefService>(source).ptr(), *pref_key);
} else {
NOTREACHED();
}
}
void ExtensionPreferenceEventRouter::OnPrefChanged(
PrefService* pref_service,
const std::string& browser_pref) {
bool incognito = (pref_service != profile_->GetPrefs());
std::string event_name;
std::string permission;
bool rv = PrefMapping::GetInstance()->FindEventForBrowserPref(
browser_pref, &event_name, &permission);
DCHECK(rv);
ListValue args;
DictionaryValue* dict = new DictionaryValue();
args.Append(dict);
const PrefService::Preference* pref =
pref_service->FindPreference(browser_pref.c_str());
CHECK(pref);
ExtensionService* extension_service = profile_->GetExtensionService();
PrefTransformerInterface* transformer =
PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
dict->Set(kValue, transformer->BrowserToExtensionPref(pref->GetValue()));
if (incognito) {
ExtensionPrefs* ep = extension_service->extension_prefs();
dict->Set(
kIncognitoSpecific,
Value::CreateBooleanValue(ep->HasIncognitoPrefValue(browser_pref)));
}
ExtensionEventRouter* router = profile_->GetExtensionEventRouter();
if (!router || !router->HasEventListener(event_name))
return;
const ExtensionList* extensions = extension_service->extensions();
for (ExtensionList::const_iterator it = extensions->begin();
it != extensions->end(); ++it) {
std::string extension_id = (*it)->id();
// TODO(bauerb): Only iterate over registered event listeners.
if (router->ExtensionHasEventListener(extension_id, event_name) &&
(*it)->HasApiPermission(permission) &&
(!incognito || extension_service->CanCrossIncognito(*it))) {
std::string level_of_control =
GetLevelOfControl(profile_, extension_id, browser_pref, incognito);
dict->Set(kLevelOfControl, Value::CreateStringValue(level_of_control));
std::string json_args;
base::JSONWriter::Write(&args, false, &json_args);
DispatchEvent(extension_id, event_name, json_args);
}
}
}
void ExtensionPreferenceEventRouter::DispatchEvent(
const std::string& extension_id,
const std::string& event_name,
const std::string& json_args) {
profile_->GetExtensionEventRouter()->DispatchEventToExtension(
extension_id, event_name, json_args, NULL, GURL());
}
// TODO(battre): Factor out common parts once this is stable.
GetPreferenceFunction::~GetPreferenceFunction() { }
bool GetPreferenceFunction::RunImpl() {
std::string pref_key;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
DictionaryValue* details = NULL;
EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
bool incognito = false;
if (details->HasKey(kIncognito))
EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito));
if (incognito && !include_incognito()) {
error_ = kIncognitoErrorMessage;
return false;
}
PrefService* prefs = incognito ? profile_->GetOffTheRecordPrefs()
: profile_->GetPrefs();
std::string browser_pref;
std::string permission;
EXTENSION_FUNCTION_VALIDATE(
PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref(
pref_key, &browser_pref, &permission));
if (!GetExtension()->HasApiPermission(permission)) {
error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str());
return false;
}
const PrefService::Preference* pref =
prefs->FindPreference(browser_pref.c_str());
CHECK(pref);
std::string level_of_control =
GetLevelOfControl(profile_, extension_id(), browser_pref, incognito);
scoped_ptr<DictionaryValue> result(new DictionaryValue);
PrefTransformerInterface* transformer =
PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
result->Set(kValue, transformer->BrowserToExtensionPref(pref->GetValue()));
result->Set(kLevelOfControl, Value::CreateStringValue(level_of_control));
if (incognito) {
ExtensionPrefs* ep = profile_->GetExtensionService()->extension_prefs();
result->Set(
kIncognitoSpecific,
Value::CreateBooleanValue(ep->HasIncognitoPrefValue(browser_pref)));
}
result_.reset(result.release());
return true;
}
SetPreferenceFunction::~SetPreferenceFunction() { }
bool SetPreferenceFunction::RunImpl() {
std::string pref_key;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
DictionaryValue* details = NULL;
EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
Value* value = NULL;
EXTENSION_FUNCTION_VALIDATE(details->Get(kValue, &value));
bool incognito = false;
if (details->HasKey(kIncognito))
EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito));
if (incognito && !include_incognito()) {
error_ = kIncognitoErrorMessage;
return false;
}
std::string browser_pref;
std::string permission;
EXTENSION_FUNCTION_VALIDATE(
PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref(
pref_key, &browser_pref, &permission));
if (!GetExtension()->HasApiPermission(permission)) {
error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str());
return false;
}
ExtensionPrefs* prefs = profile_->GetExtensionService()->extension_prefs();
const PrefService::Preference* pref =
prefs->pref_service()->FindPreference(browser_pref.c_str());
CHECK(pref);
EXTENSION_FUNCTION_VALIDATE(value->GetType() == pref->GetType());
PrefTransformerInterface* transformer =
PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
std::string error;
Value* browserPrefValue = transformer->ExtensionToBrowserPref(value, &error);
if (!browserPrefValue) {
error_ = error;
return false;
}
prefs->SetExtensionControlledPref(extension_id(),
browser_pref,
incognito,
browserPrefValue);
return true;
}
ClearPreferenceFunction::~ClearPreferenceFunction() { }
bool ClearPreferenceFunction::RunImpl() {
std::string pref_key;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
DictionaryValue* details = NULL;
EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
bool incognito = false;
if (details->HasKey(kIncognito))
EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito));
// We don't check incognito permissions here, as an extension should be always
// allowed to clear its own settings.
std::string browser_pref;
std::string permission;
EXTENSION_FUNCTION_VALIDATE(
PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref(
pref_key, &browser_pref, &permission));
if (!GetExtension()->HasApiPermission(permission)) {
error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str());
return false;
}
ExtensionPrefs* prefs = profile_->GetExtensionService()->extension_prefs();
prefs->RemoveExtensionControlledPref(extension_id(), browser_pref, incognito);
return true;
}