1 // Copyright (c) 2013 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 "extensions/common/permissions/permissions_data.h"
6
7 #include "base/command_line.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/string16.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "content/public/common/url_constants.h"
15 #include "extensions/common/constants.h"
16 #include "extensions/common/error_utils.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/extensions_client.h"
19 #include "extensions/common/features/feature.h"
20 #include "extensions/common/features/feature_provider.h"
21 #include "extensions/common/manifest.h"
22 #include "extensions/common/manifest_constants.h"
23 #include "extensions/common/manifest_handler.h"
24 #include "extensions/common/permissions/api_permission_set.h"
25 #include "extensions/common/permissions/permission_message_provider.h"
26 #include "extensions/common/permissions/permission_set.h"
27 #include "extensions/common/permissions/permissions_info.h"
28 #include "extensions/common/switches.h"
29 #include "extensions/common/url_pattern_set.h"
30 #include "extensions/common/user_script.h"
31 #include "url/gurl.h"
32
33 namespace extensions {
34
35 namespace keys = manifest_keys;
36 namespace errors = manifest_errors;
37
38 namespace {
39
40 PermissionsData::PolicyDelegate* g_policy_delegate = NULL;
41
42 // Custom checks for the experimental permission that can't be expressed in
43 // _permission_features.json.
CanSpecifyExperimentalPermission(const Extension * extension)44 bool CanSpecifyExperimentalPermission(const Extension* extension) {
45 if (extension->location() == Manifest::COMPONENT)
46 return true;
47
48 if (CommandLine::ForCurrentProcess()->HasSwitch(
49 switches::kEnableExperimentalExtensionApis)) {
50 return true;
51 }
52
53 // We rely on the webstore to check access to experimental. This way we can
54 // whitelist extensions to have access to experimental in just the store, and
55 // not have to push a new version of the client.
56 if (extension->from_webstore())
57 return true;
58
59 return false;
60 }
61
62 // Checks whether the host |pattern| is allowed for the given |extension|,
63 // given API permissions |permissions|.
CanSpecifyHostPermission(const Extension * extension,const URLPattern & pattern,const APIPermissionSet & permissions)64 bool CanSpecifyHostPermission(const Extension* extension,
65 const URLPattern& pattern,
66 const APIPermissionSet& permissions) {
67 if (!pattern.match_all_urls() &&
68 pattern.MatchesScheme(chrome::kChromeUIScheme)) {
69 URLPatternSet chrome_scheme_hosts = ExtensionsClient::Get()->
70 GetPermittedChromeSchemeHosts(extension, permissions);
71 if (chrome_scheme_hosts.ContainsPattern(pattern))
72 return true;
73
74 // Component extensions can have access to all of chrome://*.
75 if (PermissionsData::CanExecuteScriptEverywhere(extension))
76 return true;
77
78 if (CommandLine::ForCurrentProcess()->HasSwitch(
79 switches::kExtensionsOnChromeURLs)) {
80 return true;
81 }
82
83 // TODO(aboxhall): return from_webstore() when webstore handles blocking
84 // extensions which request chrome:// urls
85 return false;
86 }
87
88 // Otherwise, the valid schemes were handled by URLPattern.
89 return true;
90 }
91
92 // Parses the host and api permissions from the specified permission |key|
93 // from |extension|'s manifest.
ParseHelper(Extension * extension,const char * key,APIPermissionSet * api_permissions,URLPatternSet * host_permissions,string16 * error)94 bool ParseHelper(Extension* extension,
95 const char* key,
96 APIPermissionSet* api_permissions,
97 URLPatternSet* host_permissions,
98 string16* error) {
99 if (!extension->manifest()->HasKey(key))
100 return true;
101
102 const base::ListValue* permissions = NULL;
103 if (!extension->manifest()->GetList(key, &permissions)) {
104 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
105 std::string());
106 return false;
107 }
108
109 // NOTE: We need to get the APIPermission before we check if features
110 // associated with them are available because the feature system does not
111 // know about aliases.
112
113 std::vector<std::string> host_data;
114 if (!APIPermissionSet::ParseFromJSON(
115 permissions, APIPermissionSet::kDisallowInternalPermissions,
116 api_permissions, error, &host_data)) {
117 return false;
118 }
119
120 // Verify feature availability of permissions.
121 std::vector<APIPermission::ID> to_remove;
122 FeatureProvider* permission_features =
123 FeatureProvider::GetPermissionFeatures();
124 for (APIPermissionSet::const_iterator iter = api_permissions->begin();
125 iter != api_permissions->end(); ++iter) {
126 Feature* feature = permission_features->GetFeature(iter->name());
127
128 // The feature should exist since we just got an APIPermission for it. The
129 // two systems should be updated together whenever a permission is added.
130 DCHECK(feature);
131 // http://crbug.com/176381
132 if (!feature) {
133 to_remove.push_back(iter->id());
134 continue;
135 }
136
137 Feature::Availability availability = feature->IsAvailableToManifest(
138 extension->id(),
139 extension->GetType(),
140 Feature::ConvertLocation(extension->location()),
141 extension->manifest_version());
142
143 if (!availability.is_available()) {
144 // Don't fail, but warn the developer that the manifest contains
145 // unrecognized permissions. This may happen legitimately if the
146 // extensions requests platform- or channel-specific permissions.
147 extension->AddInstallWarning(InstallWarning(availability.message(),
148 feature->name()));
149 to_remove.push_back(iter->id());
150 continue;
151 }
152
153 if (iter->id() == APIPermission::kExperimental) {
154 if (!CanSpecifyExperimentalPermission(extension)) {
155 *error = ASCIIToUTF16(errors::kExperimentalFlagRequired);
156 return false;
157 }
158 }
159 }
160
161 api_permissions->AddImpliedPermissions();
162
163 // Remove permissions that are not available to this extension.
164 for (std::vector<APIPermission::ID>::const_iterator iter = to_remove.begin();
165 iter != to_remove.end(); ++iter) {
166 api_permissions->erase(*iter);
167 }
168
169 // Parse host pattern permissions.
170 const int kAllowedSchemes =
171 PermissionsData::CanExecuteScriptEverywhere(extension) ?
172 URLPattern::SCHEME_ALL : Extension::kValidHostPermissionSchemes;
173
174 for (std::vector<std::string>::const_iterator iter = host_data.begin();
175 iter != host_data.end(); ++iter) {
176 const std::string& permission_str = *iter;
177
178 // Check if it's a host pattern permission.
179 URLPattern pattern = URLPattern(kAllowedSchemes);
180 URLPattern::ParseResult parse_result = pattern.Parse(permission_str);
181 if (parse_result == URLPattern::PARSE_SUCCESS) {
182 // The path component is not used for host permissions, so we force it
183 // to match all paths.
184 pattern.SetPath("/*");
185 int valid_schemes = pattern.valid_schemes();
186 if (pattern.MatchesScheme(chrome::kFileScheme) &&
187 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
188 extension->set_wants_file_access(true);
189 if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS))
190 valid_schemes &= ~URLPattern::SCHEME_FILE;
191 }
192
193 if (pattern.scheme() != chrome::kChromeUIScheme &&
194 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
195 // Keep chrome:// in allowed schemes only if it's explicitly requested
196 // or CanExecuteScriptEverywhere is true. If the
197 // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
198 // will fail, so don't check the flag here.
199 valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
200 }
201 pattern.SetValidSchemes(valid_schemes);
202
203 if (!CanSpecifyHostPermission(extension, pattern, *api_permissions)) {
204 // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
205 // below).
206 extension->AddInstallWarning(InstallWarning(
207 ErrorUtils::FormatErrorMessage(
208 errors::kInvalidPermissionScheme, permission_str),
209 key,
210 permission_str));
211 continue;
212 }
213
214 host_permissions->AddPattern(pattern);
215 // We need to make sure all_urls matches chrome://favicon and (maybe)
216 // chrome://thumbnail, so add them back in to host_permissions separately.
217 if (pattern.match_all_urls()) {
218 host_permissions->AddPatterns(
219 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
220 extension, *api_permissions));
221 }
222 continue;
223 }
224
225 // It's probably an unknown API permission. Do not throw an error so
226 // extensions can retain backwards compatability (http://crbug.com/42742).
227 extension->AddInstallWarning(InstallWarning(
228 ErrorUtils::FormatErrorMessage(
229 manifest_errors::kPermissionUnknownOrMalformed,
230 permission_str),
231 key,
232 permission_str));
233 }
234
235 return true;
236 }
237
238 // Returns true if this extension id is from a trusted provider.
IsTrustedId(const std::string & extension_id)239 bool IsTrustedId(const std::string& extension_id) {
240 // See http://b/4946060 for more details.
241 return extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd");
242 }
243
244 } // namespace
245
246 struct PermissionsData::InitialPermissions {
247 APIPermissionSet api_permissions;
248 ManifestPermissionSet manifest_permissions;
249 URLPatternSet host_permissions;
250 URLPatternSet scriptable_hosts;
251 };
252
PermissionsData()253 PermissionsData::PermissionsData() {
254 }
255
~PermissionsData()256 PermissionsData::~PermissionsData() {
257 }
258
259 // static
SetPolicyDelegate(PolicyDelegate * delegate)260 void PermissionsData::SetPolicyDelegate(PolicyDelegate* delegate) {
261 g_policy_delegate = delegate;
262 }
263
264 // static
GetOptionalPermissions(const Extension * extension)265 const PermissionSet* PermissionsData::GetOptionalPermissions(
266 const Extension* extension) {
267 return extension->permissions_data()->optional_permission_set_.get();
268 }
269
270 // static
GetRequiredPermissions(const Extension * extension)271 const PermissionSet* PermissionsData::GetRequiredPermissions(
272 const Extension* extension) {
273 return extension->permissions_data()->required_permission_set_.get();
274 }
275
276 // static
GetInitialAPIPermissions(const Extension * extension)277 const APIPermissionSet* PermissionsData::GetInitialAPIPermissions(
278 const Extension* extension) {
279 return &extension->permissions_data()->
280 initial_required_permissions_->api_permissions;
281 }
282
283 // static
GetInitialAPIPermissions(Extension * extension)284 APIPermissionSet* PermissionsData::GetInitialAPIPermissions(
285 Extension* extension) {
286 return &extension->permissions_data()->
287 initial_required_permissions_->api_permissions;
288 }
289
290 // static
SetInitialScriptableHosts(Extension * extension,const URLPatternSet & scriptable_hosts)291 void PermissionsData::SetInitialScriptableHosts(
292 Extension* extension, const URLPatternSet& scriptable_hosts) {
293 extension->permissions_data()->
294 initial_required_permissions_->scriptable_hosts = scriptable_hosts;
295 }
296
297 // static
SetActivePermissions(const Extension * extension,const PermissionSet * permissions)298 void PermissionsData::SetActivePermissions(const Extension* extension,
299 const PermissionSet* permissions) {
300 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
301 extension->permissions_data()->active_permissions_ = permissions;
302 }
303
304 // static
GetActivePermissions(const Extension * extension)305 scoped_refptr<const PermissionSet> PermissionsData::GetActivePermissions(
306 const Extension* extension) {
307 return extension->permissions_data()->active_permissions_;
308 }
309
310 // static
GetTabSpecificPermissions(const Extension * extension,int tab_id)311 scoped_refptr<const PermissionSet> PermissionsData::GetTabSpecificPermissions(
312 const Extension* extension,
313 int tab_id) {
314 CHECK_GE(tab_id, 0);
315 TabPermissionsMap::const_iterator iter =
316 extension->permissions_data()->tab_specific_permissions_.find(tab_id);
317 return
318 (iter != extension->permissions_data()->tab_specific_permissions_.end())
319 ? iter->second
320 : NULL;
321 }
322
323 // static
UpdateTabSpecificPermissions(const Extension * extension,int tab_id,scoped_refptr<const PermissionSet> permissions)324 void PermissionsData::UpdateTabSpecificPermissions(
325 const Extension* extension,
326 int tab_id,
327 scoped_refptr<const PermissionSet> permissions) {
328 CHECK_GE(tab_id, 0);
329 TabPermissionsMap* tab_permissions =
330 &extension->permissions_data()->tab_specific_permissions_;
331 if (tab_permissions->count(tab_id)) {
332 (*tab_permissions)[tab_id] = PermissionSet::CreateUnion(
333 (*tab_permissions)[tab_id].get(), permissions.get());
334 } else {
335 (*tab_permissions)[tab_id] = permissions;
336 }
337 }
338
339 // static
ClearTabSpecificPermissions(const Extension * extension,int tab_id)340 void PermissionsData::ClearTabSpecificPermissions(
341 const Extension* extension,
342 int tab_id) {
343 CHECK_GE(tab_id, 0);
344 extension->permissions_data()->tab_specific_permissions_.erase(tab_id);
345 }
346
347 // static
HasAPIPermission(const Extension * extension,APIPermission::ID permission)348 bool PermissionsData::HasAPIPermission(const Extension* extension,
349 APIPermission::ID permission) {
350 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
351 return GetActivePermissions(extension)->HasAPIPermission(permission);
352 }
353
354 // static
HasAPIPermission(const Extension * extension,const std::string & permission_name)355 bool PermissionsData::HasAPIPermission(
356 const Extension* extension,
357 const std::string& permission_name) {
358 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
359 return GetActivePermissions(extension)->HasAPIPermission(permission_name);
360 }
361
362 // static
HasAPIPermissionForTab(const Extension * extension,int tab_id,APIPermission::ID permission)363 bool PermissionsData::HasAPIPermissionForTab(
364 const Extension* extension,
365 int tab_id,
366 APIPermission::ID permission) {
367 if (HasAPIPermission(extension, permission))
368 return true;
369
370 // Place autolock below the HasAPIPermission() check, since HasAPIPermission
371 // also acquires the lock.
372 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
373 scoped_refptr<const PermissionSet> tab_permissions =
374 GetTabSpecificPermissions(extension, tab_id);
375 return tab_permissions.get() && tab_permissions->HasAPIPermission(permission);
376 }
377
378 // static
CheckAPIPermissionWithParam(const Extension * extension,APIPermission::ID permission,const APIPermission::CheckParam * param)379 bool PermissionsData::CheckAPIPermissionWithParam(
380 const Extension* extension,
381 APIPermission::ID permission,
382 const APIPermission::CheckParam* param) {
383 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
384 return GetActivePermissions(extension)->CheckAPIPermissionWithParam(
385 permission, param);
386 }
387
388 // static
GetEffectiveHostPermissions(const Extension * extension)389 const URLPatternSet& PermissionsData::GetEffectiveHostPermissions(
390 const Extension* extension) {
391 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
392 return GetActivePermissions(extension)->effective_hosts();
393 }
394
395 // static
CanSilentlyIncreasePermissions(const Extension * extension)396 bool PermissionsData::CanSilentlyIncreasePermissions(
397 const Extension* extension) {
398 return extension->location() != Manifest::INTERNAL;
399 }
400
401 // static
ShouldSkipPermissionWarnings(const Extension * extension)402 bool PermissionsData::ShouldSkipPermissionWarnings(const Extension* extension) {
403 return IsTrustedId(extension->id());
404 }
405
406 // static
HasHostPermission(const Extension * extension,const GURL & url)407 bool PermissionsData::HasHostPermission(const Extension* extension,
408 const GURL& url) {
409 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
410 return GetActivePermissions(extension)->HasExplicitAccessToOrigin(url);
411 }
412
413 // static
HasEffectiveAccessToAllHosts(const Extension * extension)414 bool PermissionsData::HasEffectiveAccessToAllHosts(const Extension* extension) {
415 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
416 return GetActivePermissions(extension)->HasEffectiveAccessToAllHosts();
417 }
418
419 // static
GetPermissionMessages(const Extension * extension)420 PermissionMessages PermissionsData::GetPermissionMessages(
421 const Extension* extension) {
422 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
423 if (ShouldSkipPermissionWarnings(extension)) {
424 return PermissionMessages();
425 } else {
426 return PermissionMessageProvider::Get()->GetPermissionMessages(
427 GetActivePermissions(extension), extension->GetType());
428 }
429 }
430
431 // static
GetPermissionMessageStrings(const Extension * extension)432 std::vector<string16> PermissionsData::GetPermissionMessageStrings(
433 const Extension* extension) {
434 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
435 if (ShouldSkipPermissionWarnings(extension)) {
436 return std::vector<string16>();
437 } else {
438 return PermissionMessageProvider::Get()->GetWarningMessages(
439 GetActivePermissions(extension), extension->GetType());
440 }
441 }
442
443 // static
GetPermissionMessageDetailsStrings(const Extension * extension)444 std::vector<string16> PermissionsData::GetPermissionMessageDetailsStrings(
445 const Extension* extension) {
446 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
447 if (ShouldSkipPermissionWarnings(extension)) {
448 return std::vector<string16>();
449 } else {
450 return PermissionMessageProvider::Get()->GetWarningMessagesDetails(
451 GetActivePermissions(extension), extension->GetType());
452 }
453 }
454
455 // static
CanExecuteScriptOnPage(const Extension * extension,const GURL & document_url,const GURL & top_frame_url,int tab_id,const UserScript * script,int process_id,std::string * error)456 bool PermissionsData::CanExecuteScriptOnPage(const Extension* extension,
457 const GURL& document_url,
458 const GURL& top_frame_url,
459 int tab_id,
460 const UserScript* script,
461 int process_id,
462 std::string* error) {
463 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
464 const CommandLine* command_line = CommandLine::ForCurrentProcess();
465 bool can_execute_everywhere = CanExecuteScriptEverywhere(extension);
466
467 if (g_policy_delegate &&
468 !g_policy_delegate->CanExecuteScriptOnPage(
469 extension, document_url, top_frame_url, tab_id,
470 script, process_id, error))
471 return false;
472
473 if (!can_execute_everywhere &&
474 !ExtensionsClient::Get()->IsScriptableURL(document_url, error)) {
475 return false;
476 }
477
478 if (!command_line->HasSwitch(switches::kExtensionsOnChromeURLs)) {
479 if (document_url.SchemeIs(chrome::kChromeUIScheme) &&
480 !can_execute_everywhere) {
481 if (error)
482 *error = errors::kCannotAccessChromeUrl;
483 return false;
484 }
485 }
486
487 if (top_frame_url.SchemeIs(extensions::kExtensionScheme) &&
488 top_frame_url.GetOrigin() !=
489 Extension::GetBaseURLFromExtensionId(extension->id()).GetOrigin() &&
490 !can_execute_everywhere) {
491 if (error)
492 *error = errors::kCannotAccessExtensionUrl;
493 return false;
494 }
495
496 // If a tab ID is specified, try the tab-specific permissions.
497 if (tab_id >= 0) {
498 scoped_refptr<const PermissionSet> tab_permissions =
499 GetTabSpecificPermissions(extension, tab_id);
500 if (tab_permissions.get() &&
501 tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) {
502 return true;
503 }
504 }
505
506 bool can_access = false;
507
508 if (script) {
509 // If a script is specified, use its matches.
510 can_access = script->MatchesURL(document_url);
511 } else {
512 // Otherwise, see if this extension has permission to execute script
513 // programmatically on pages.
514 can_access = GetActivePermissions(extension)->
515 HasExplicitAccessToOrigin(document_url);
516 }
517
518 if (!can_access && error) {
519 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
520 document_url.spec());
521 }
522
523 return can_access;
524 }
525
526 // static
CanExecuteScriptEverywhere(const Extension * extension)527 bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) {
528 if (extension->location() == Manifest::COMPONENT)
529 return true;
530
531 const ExtensionsClient::ScriptingWhitelist& whitelist =
532 ExtensionsClient::Get()->GetScriptingWhitelist();
533
534 return std::find(whitelist.begin(), whitelist.end(), extension->id()) !=
535 whitelist.end();
536 }
537
538 // static
CanCaptureVisiblePage(const Extension * extension,const GURL & page_url,int tab_id,std::string * error)539 bool PermissionsData::CanCaptureVisiblePage(const Extension* extension,
540 const GURL& page_url,
541 int tab_id,
542 std::string* error) {
543 if (tab_id >= 0) {
544 scoped_refptr<const PermissionSet> tab_permissions =
545 GetTabSpecificPermissions(extension, tab_id);
546 if (tab_permissions.get() &&
547 tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) {
548 return true;
549 }
550 }
551
552 if (HasHostPermission(extension, page_url) ||
553 page_url.GetOrigin() == extension->url()) {
554 return true;
555 }
556
557 if (error) {
558 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
559 page_url.spec());
560 }
561 return false;
562 }
563
ParsePermissions(Extension * extension,string16 * error)564 bool PermissionsData::ParsePermissions(Extension* extension, string16* error) {
565 initial_required_permissions_.reset(new InitialPermissions);
566 if (!ParseHelper(extension,
567 keys::kPermissions,
568 &initial_required_permissions_->api_permissions,
569 &initial_required_permissions_->host_permissions,
570 error)) {
571 return false;
572 }
573
574 // TODO(jeremya/kalman) do this via the features system by exposing the
575 // app.window API to platform apps, with no dependency on any permissions.
576 // See http://crbug.com/120069.
577 if (extension->is_platform_app()) {
578 initial_required_permissions_->api_permissions.insert(
579 APIPermission::kAppCurrentWindowInternal);
580 initial_required_permissions_->api_permissions.insert(
581 APIPermission::kAppRuntime);
582 initial_required_permissions_->api_permissions.insert(
583 APIPermission::kAppWindow);
584 }
585
586 initial_optional_permissions_.reset(new InitialPermissions);
587 if (!ParseHelper(extension,
588 keys::kOptionalPermissions,
589 &initial_optional_permissions_->api_permissions,
590 &initial_optional_permissions_->host_permissions,
591 error)) {
592 return false;
593 }
594
595 return true;
596 }
597
InitializeManifestPermissions(Extension * extension)598 void PermissionsData::InitializeManifestPermissions(Extension* extension) {
599 ManifestHandler::AddExtensionInitialRequiredPermissions(
600 extension, &initial_required_permissions_->manifest_permissions);
601 }
602
FinalizePermissions(Extension * extension)603 void PermissionsData::FinalizePermissions(Extension* extension) {
604 active_permissions_ = new PermissionSet(
605 initial_required_permissions_->api_permissions,
606 initial_required_permissions_->manifest_permissions,
607 initial_required_permissions_->host_permissions,
608 initial_required_permissions_->scriptable_hosts);
609
610 required_permission_set_ = new PermissionSet(
611 initial_required_permissions_->api_permissions,
612 initial_required_permissions_->manifest_permissions,
613 initial_required_permissions_->host_permissions,
614 initial_required_permissions_->scriptable_hosts);
615
616 optional_permission_set_ = new PermissionSet(
617 initial_optional_permissions_->api_permissions,
618 initial_optional_permissions_->manifest_permissions,
619 initial_optional_permissions_->host_permissions,
620 URLPatternSet());
621
622 initial_required_permissions_.reset();
623 initial_optional_permissions_.reset();
624 }
625
626 } // namespace extensions
627