1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/extension_preference_api.h"
6
7 #include <map>
8
9 #include "base/json/json_writer.h"
10 #include "base/memory/singleton.h"
11 #include "base/stl_util-inl.h"
12 #include "base/stringprintf.h"
13 #include "base/values.h"
14 #include "chrome/browser/extensions/extension_event_router.h"
15 #include "chrome/browser/extensions/extension_prefs.h"
16 #include "chrome/browser/extensions/extension_proxy_api.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/pref_names.h"
20 #include "content/common/notification_type.h"
21 #include "content/common/notification_service.h"
22
23 namespace {
24
25 struct PrefMappingEntry {
26 const char* extension_pref;
27 const char* browser_pref;
28 const char* permission;
29 };
30
31 const char kNotControllable[] = "NotControllable";
32 const char kControlledByOtherExtensions[] = "ControlledByOtherExtensions";
33 const char kControllableByThisExtension[] = "ControllableByThisExtension";
34 const char kControlledByThisExtension[] = "ControlledByThisExtension";
35
36 const char kIncognito[] = "incognito";
37 const char kIncognitoSpecific[] = "incognitoSpecific";
38 const char kLevelOfControl[] = "levelOfControl";
39 const char kValue[] = "value";
40
41 const char kOnPrefChangeFormat[] = "experimental.preferences.%s.onChange";
42
43 const char kIncognitoErrorMessage[] =
44 "You do not have permission to access incognito preferences.";
45
46 const char kPermissionErrorMessage[] =
47 "You do not have permission to access the preference '%s'. "
48 "Be sure to declare in your manifest what permissions you need.";
49
50 PrefMappingEntry kPrefMapping[] = {
51 { "blockThirdPartyCookies",
52 prefs::kBlockThirdPartyCookies,
53 Extension::kContentSettingsPermission
54 },
55 { "enableReferrers",
56 prefs::kEnableReferrers,
57 Extension::kContentSettingsPermission
58 },
59 { "enableHyperlinkAuditing",
60 prefs::kEnableHyperlinkAuditing,
61 Extension::kContentSettingsPermission
62 },
63 { "proxy",
64 prefs::kProxy,
65 Extension::kProxyPermission
66 },
67 };
68
69 class IdentityPrefTransformer : public PrefTransformerInterface {
70 public:
IdentityPrefTransformer()71 IdentityPrefTransformer() { }
~IdentityPrefTransformer()72 virtual ~IdentityPrefTransformer() { }
73
ExtensionToBrowserPref(const Value * extension_pref,std::string * error)74 virtual Value* ExtensionToBrowserPref(const Value* extension_pref,
75 std::string* error) {
76 return extension_pref->DeepCopy();
77 }
78
BrowserToExtensionPref(const Value * browser_pref)79 virtual Value* BrowserToExtensionPref(const Value* browser_pref) {
80 return browser_pref->DeepCopy();
81 }
82 };
83
84 // Returns a string constant (defined in the API) indicating the level of
85 // control this extension has over the specified preference.
GetLevelOfControl(Profile * profile,const std::string & extension_id,const std::string & browser_pref,bool incognito)86 const char* GetLevelOfControl(
87 Profile* profile,
88 const std::string& extension_id,
89 const std::string& browser_pref,
90 bool incognito) {
91 PrefService* prefs = incognito ? profile->GetOffTheRecordPrefs()
92 : profile->GetPrefs();
93 const PrefService::Preference* pref =
94 prefs->FindPreference(browser_pref.c_str());
95 CHECK(pref);
96 ExtensionPrefs* ep = profile->GetExtensionService()->extension_prefs();
97
98 if (!pref->IsExtensionModifiable())
99 return kNotControllable;
100
101 if (ep->DoesExtensionControlPref(extension_id, browser_pref, incognito))
102 return kControlledByThisExtension;
103
104 if (ep->CanExtensionControlPref(extension_id, browser_pref, incognito))
105 return kControllableByThisExtension;
106
107 return kControlledByOtherExtensions;
108 }
109
110 class PrefMapping {
111 public:
GetInstance()112 static PrefMapping* GetInstance() {
113 return Singleton<PrefMapping>::get();
114 }
115
FindBrowserPrefForExtensionPref(const std::string & extension_pref,std::string * browser_pref,std::string * permission)116 bool FindBrowserPrefForExtensionPref(const std::string& extension_pref,
117 std::string* browser_pref,
118 std::string* permission) {
119 std::map<std::string, std::pair<std::string, std::string> >::iterator it =
120 mapping_.find(extension_pref);
121 if (it != mapping_.end()) {
122 *browser_pref = it->second.first;
123 *permission = it->second.second;
124 return true;
125 }
126 return false;
127 }
128
FindEventForBrowserPref(const std::string & browser_pref,std::string * event_name,std::string * permission)129 bool FindEventForBrowserPref(const std::string& browser_pref,
130 std::string* event_name,
131 std::string* permission) {
132 std::map<std::string, std::pair<std::string, std::string> >::iterator it =
133 event_mapping_.find(browser_pref);
134 if (it != event_mapping_.end()) {
135 *event_name = it->second.first;
136 *permission = it->second.second;
137 return true;
138 }
139 return false;
140 }
141
FindTransformerForBrowserPref(const std::string & browser_pref)142 PrefTransformerInterface* FindTransformerForBrowserPref(
143 const std::string& browser_pref) {
144 std::map<std::string, PrefTransformerInterface*>::iterator it =
145 transformers_.find(browser_pref);
146 if (it != transformers_.end())
147 return it->second;
148 else
149 return identity_transformer_.get();
150 }
151
152 private:
153 friend struct DefaultSingletonTraits<PrefMapping>;
154
PrefMapping()155 PrefMapping() {
156 identity_transformer_.reset(new IdentityPrefTransformer());
157 for (size_t i = 0; i < arraysize(kPrefMapping); ++i) {
158 mapping_[kPrefMapping[i].extension_pref] =
159 std::make_pair(kPrefMapping[i].browser_pref,
160 kPrefMapping[i].permission);
161 std::string event_name =
162 base::StringPrintf(kOnPrefChangeFormat,
163 kPrefMapping[i].extension_pref);
164 event_mapping_[kPrefMapping[i].browser_pref] =
165 std::make_pair(event_name, kPrefMapping[i].permission);
166 }
167 DCHECK_EQ(arraysize(kPrefMapping), mapping_.size());
168 DCHECK_EQ(arraysize(kPrefMapping), event_mapping_.size());
169 RegisterPrefTransformer(prefs::kProxy, new ProxyPrefTransformer());
170 }
171
~PrefMapping()172 ~PrefMapping() {
173 STLDeleteContainerPairSecondPointers(transformers_.begin(),
174 transformers_.end());
175 }
176
RegisterPrefTransformer(const std::string & browser_pref,PrefTransformerInterface * transformer)177 void RegisterPrefTransformer(const std::string& browser_pref,
178 PrefTransformerInterface* transformer) {
179 DCHECK_EQ(0u, transformers_.count(browser_pref)) <<
180 "Trying to register pref transformer for " << browser_pref << " twice";
181 transformers_[browser_pref] = transformer;
182 }
183
184 // Mapping from extension pref keys to browser pref keys and permissions.
185 std::map<std::string, std::pair<std::string, std::string> > mapping_;
186
187 // Mapping from browser pref keys to extension event names and permissions.
188 std::map<std::string, std::pair<std::string, std::string> > event_mapping_;
189
190 // Mapping from browser pref keys to transformers.
191 std::map<std::string, PrefTransformerInterface*> transformers_;
192
193 scoped_ptr<PrefTransformerInterface> identity_transformer_;
194
195 DISALLOW_COPY_AND_ASSIGN(PrefMapping);
196 };
197
198 } // namespace
199
ExtensionPreferenceEventRouter(Profile * profile)200 ExtensionPreferenceEventRouter::ExtensionPreferenceEventRouter(
201 Profile* profile) : profile_(profile) {
202 registrar_.Init(profile_->GetPrefs());
203 incognito_registrar_.Init(profile_->GetOffTheRecordPrefs());
204 for (size_t i = 0; i < arraysize(kPrefMapping); ++i) {
205 registrar_.Add(kPrefMapping[i].browser_pref, this);
206 incognito_registrar_.Add(kPrefMapping[i].browser_pref, this);
207 }
208 }
209
~ExtensionPreferenceEventRouter()210 ExtensionPreferenceEventRouter::~ExtensionPreferenceEventRouter() { }
211
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)212 void ExtensionPreferenceEventRouter::Observe(
213 NotificationType type,
214 const NotificationSource& source,
215 const NotificationDetails& details) {
216 if (type == NotificationType::PREF_CHANGED) {
217 const std::string* pref_key =
218 Details<const std::string>(details).ptr();
219 OnPrefChanged(Source<PrefService>(source).ptr(), *pref_key);
220 } else {
221 NOTREACHED();
222 }
223 }
224
OnPrefChanged(PrefService * pref_service,const std::string & browser_pref)225 void ExtensionPreferenceEventRouter::OnPrefChanged(
226 PrefService* pref_service,
227 const std::string& browser_pref) {
228 bool incognito = (pref_service != profile_->GetPrefs());
229
230 std::string event_name;
231 std::string permission;
232 bool rv = PrefMapping::GetInstance()->FindEventForBrowserPref(
233 browser_pref, &event_name, &permission);
234 DCHECK(rv);
235
236 ListValue args;
237 DictionaryValue* dict = new DictionaryValue();
238 args.Append(dict);
239 const PrefService::Preference* pref =
240 pref_service->FindPreference(browser_pref.c_str());
241 CHECK(pref);
242 ExtensionService* extension_service = profile_->GetExtensionService();
243 PrefTransformerInterface* transformer =
244 PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
245 dict->Set(kValue, transformer->BrowserToExtensionPref(pref->GetValue()));
246 if (incognito) {
247 ExtensionPrefs* ep = extension_service->extension_prefs();
248 dict->Set(
249 kIncognitoSpecific,
250 Value::CreateBooleanValue(ep->HasIncognitoPrefValue(browser_pref)));
251 }
252
253 ExtensionEventRouter* router = profile_->GetExtensionEventRouter();
254 if (!router || !router->HasEventListener(event_name))
255 return;
256 const ExtensionList* extensions = extension_service->extensions();
257 for (ExtensionList::const_iterator it = extensions->begin();
258 it != extensions->end(); ++it) {
259 std::string extension_id = (*it)->id();
260 // TODO(bauerb): Only iterate over registered event listeners.
261 if (router->ExtensionHasEventListener(extension_id, event_name) &&
262 (*it)->HasApiPermission(permission) &&
263 (!incognito || extension_service->CanCrossIncognito(*it))) {
264 std::string level_of_control =
265 GetLevelOfControl(profile_, extension_id, browser_pref, incognito);
266 dict->Set(kLevelOfControl, Value::CreateStringValue(level_of_control));
267
268 std::string json_args;
269 base::JSONWriter::Write(&args, false, &json_args);
270
271 DispatchEvent(extension_id, event_name, json_args);
272 }
273 }
274 }
275
DispatchEvent(const std::string & extension_id,const std::string & event_name,const std::string & json_args)276 void ExtensionPreferenceEventRouter::DispatchEvent(
277 const std::string& extension_id,
278 const std::string& event_name,
279 const std::string& json_args) {
280 profile_->GetExtensionEventRouter()->DispatchEventToExtension(
281 extension_id, event_name, json_args, NULL, GURL());
282 }
283
284 // TODO(battre): Factor out common parts once this is stable.
285
~GetPreferenceFunction()286 GetPreferenceFunction::~GetPreferenceFunction() { }
287
RunImpl()288 bool GetPreferenceFunction::RunImpl() {
289 std::string pref_key;
290 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
291 DictionaryValue* details = NULL;
292 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
293
294 bool incognito = false;
295 if (details->HasKey(kIncognito))
296 EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito));
297
298 if (incognito && !include_incognito()) {
299 error_ = kIncognitoErrorMessage;
300 return false;
301 }
302
303 PrefService* prefs = incognito ? profile_->GetOffTheRecordPrefs()
304 : profile_->GetPrefs();
305 std::string browser_pref;
306 std::string permission;
307 EXTENSION_FUNCTION_VALIDATE(
308 PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref(
309 pref_key, &browser_pref, &permission));
310 if (!GetExtension()->HasApiPermission(permission)) {
311 error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str());
312 return false;
313 }
314
315 const PrefService::Preference* pref =
316 prefs->FindPreference(browser_pref.c_str());
317 CHECK(pref);
318 std::string level_of_control =
319 GetLevelOfControl(profile_, extension_id(), browser_pref, incognito);
320
321 scoped_ptr<DictionaryValue> result(new DictionaryValue);
322 PrefTransformerInterface* transformer =
323 PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
324 result->Set(kValue, transformer->BrowserToExtensionPref(pref->GetValue()));
325 result->Set(kLevelOfControl, Value::CreateStringValue(level_of_control));
326 if (incognito) {
327 ExtensionPrefs* ep = profile_->GetExtensionService()->extension_prefs();
328 result->Set(
329 kIncognitoSpecific,
330 Value::CreateBooleanValue(ep->HasIncognitoPrefValue(browser_pref)));
331 }
332 result_.reset(result.release());
333 return true;
334 }
335
~SetPreferenceFunction()336 SetPreferenceFunction::~SetPreferenceFunction() { }
337
RunImpl()338 bool SetPreferenceFunction::RunImpl() {
339 std::string pref_key;
340 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
341 DictionaryValue* details = NULL;
342 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
343
344 Value* value = NULL;
345 EXTENSION_FUNCTION_VALIDATE(details->Get(kValue, &value));
346
347 bool incognito = false;
348 if (details->HasKey(kIncognito))
349 EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito));
350
351 if (incognito && !include_incognito()) {
352 error_ = kIncognitoErrorMessage;
353 return false;
354 }
355
356 std::string browser_pref;
357 std::string permission;
358 EXTENSION_FUNCTION_VALIDATE(
359 PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref(
360 pref_key, &browser_pref, &permission));
361 if (!GetExtension()->HasApiPermission(permission)) {
362 error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str());
363 return false;
364 }
365 ExtensionPrefs* prefs = profile_->GetExtensionService()->extension_prefs();
366 const PrefService::Preference* pref =
367 prefs->pref_service()->FindPreference(browser_pref.c_str());
368 CHECK(pref);
369 EXTENSION_FUNCTION_VALIDATE(value->GetType() == pref->GetType());
370 PrefTransformerInterface* transformer =
371 PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
372 std::string error;
373 Value* browserPrefValue = transformer->ExtensionToBrowserPref(value, &error);
374 if (!browserPrefValue) {
375 error_ = error;
376 return false;
377 }
378 prefs->SetExtensionControlledPref(extension_id(),
379 browser_pref,
380 incognito,
381 browserPrefValue);
382 return true;
383 }
384
~ClearPreferenceFunction()385 ClearPreferenceFunction::~ClearPreferenceFunction() { }
386
RunImpl()387 bool ClearPreferenceFunction::RunImpl() {
388 std::string pref_key;
389 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &pref_key));
390 DictionaryValue* details = NULL;
391 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &details));
392
393 bool incognito = false;
394 if (details->HasKey(kIncognito))
395 EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(kIncognito, &incognito));
396
397 // We don't check incognito permissions here, as an extension should be always
398 // allowed to clear its own settings.
399
400 std::string browser_pref;
401 std::string permission;
402 EXTENSION_FUNCTION_VALIDATE(
403 PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref(
404 pref_key, &browser_pref, &permission));
405 if (!GetExtension()->HasApiPermission(permission)) {
406 error_ = base::StringPrintf(kPermissionErrorMessage, pref_key.c_str());
407 return false;
408 }
409 ExtensionPrefs* prefs = profile_->GetExtensionService()->extension_prefs();
410 prefs->RemoveExtensionControlledPref(extension_id(), browser_pref, incognito);
411 return true;
412 }
413