| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/common/extensions/extension.h" | 5 #include "chrome/common/extensions/extension.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/basictypes.h" | 8 #include "base/basictypes.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/file_path.h" | 10 #include "base/file_path.h" |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 } | 138 } |
| 139 ~ExtensionConfig() { } | 139 ~ExtensionConfig() { } |
| 140 | 140 |
| 141 // A whitelist of extensions that can script anywhere. Do not add to this | 141 // A whitelist of extensions that can script anywhere. Do not add to this |
| 142 // list (except in tests) without consulting the Extensions team first. | 142 // list (except in tests) without consulting the Extensions team first. |
| 143 // Note: Component extensions have this right implicitly and do not need to be | 143 // Note: Component extensions have this right implicitly and do not need to be |
| 144 // added to this list. | 144 // added to this list. |
| 145 Extension::ScriptingWhitelist scripting_whitelist_; | 145 Extension::ScriptingWhitelist scripting_whitelist_; |
| 146 }; | 146 }; |
| 147 | 147 |
| 148 bool ReadLaunchDimension(const extensions::Manifest* manifest, | |
| 149 const char* key, | |
| 150 int* target, | |
| 151 bool is_valid_container, | |
| 152 string16* error) { | |
| 153 Value* temp = NULL; | |
| 154 if (manifest->Get(key, &temp)) { | |
| 155 if (!is_valid_container) { | |
| 156 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 157 errors::kInvalidLaunchValueContainer, | |
| 158 key); | |
| 159 return false; | |
| 160 } | |
| 161 if (!temp->GetAsInteger(target) || *target < 0) { | |
| 162 *target = 0; | |
| 163 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 164 errors::kInvalidLaunchValue, | |
| 165 key); | |
| 166 return false; | |
| 167 } | |
| 168 } | |
| 169 return true; | |
| 170 } | |
| 171 | |
| 172 bool ContainsManifestForbiddenPermission(const APIPermissionSet& apis, | 148 bool ContainsManifestForbiddenPermission(const APIPermissionSet& apis, |
| 173 string16* error) { | 149 string16* error) { |
| 174 CHECK(error); | 150 CHECK(error); |
| 175 for (APIPermissionSet::const_iterator i = apis.begin(); | 151 for (APIPermissionSet::const_iterator i = apis.begin(); |
| 176 i != apis.end(); ++i) { | 152 i != apis.end(); ++i) { |
| 177 if ((*i)->ManifestEntryForbidden()) { | 153 if ((*i)->ManifestEntryForbidden()) { |
| 178 *error = ErrorUtils::FormatErrorMessageUTF16( | 154 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 179 errors::kPermissionNotAllowedInManifest, | 155 errors::kPermissionNotAllowedInManifest, |
| 180 (*i)->info()->name()); | 156 (*i)->info()->name()); |
| 181 return true; | 157 return true; |
| (...skipping 654 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 836 std::string path = icons().Get(size, match_type); | 812 std::string path = icons().Get(size, match_type); |
| 837 return path.empty() ? ExtensionResource() : GetResource(path); | 813 return path.empty() ? ExtensionResource() : GetResource(path); |
| 838 } | 814 } |
| 839 | 815 |
| 840 GURL Extension::GetIconURL(int size, | 816 GURL Extension::GetIconURL(int size, |
| 841 ExtensionIconSet::MatchType match_type) const { | 817 ExtensionIconSet::MatchType match_type) const { |
| 842 std::string path = icons().Get(size, match_type); | 818 std::string path = icons().Get(size, match_type); |
| 843 return path.empty() ? GURL() : GetResourceURL(path); | 819 return path.empty() ? GURL() : GetResourceURL(path); |
| 844 } | 820 } |
| 845 | 821 |
| 846 GURL Extension::GetFullLaunchURL() const { | |
| 847 return launch_local_path().empty() ? GURL(launch_web_url()) : | |
| 848 url().Resolve(launch_local_path()); | |
| 849 } | |
| 850 | |
| 851 bool Extension::CanExecuteScriptOnPage(const GURL& document_url, | 822 bool Extension::CanExecuteScriptOnPage(const GURL& document_url, |
| 852 const GURL& top_frame_url, | 823 const GURL& top_frame_url, |
| 853 int tab_id, | 824 int tab_id, |
| 854 const UserScript* script, | 825 const UserScript* script, |
| 855 std::string* error) const { | 826 std::string* error) const { |
| 856 base::AutoLock auto_lock(runtime_data_lock_); | 827 base::AutoLock auto_lock(runtime_data_lock_); |
| 857 // The gallery is special-cased as a restricted URL for scripting to prevent | 828 // The gallery is special-cased as a restricted URL for scripting to prevent |
| 858 // access to special JS bindings we expose to the gallery (and avoid things | 829 // access to special JS bindings we expose to the gallery (and avoid things |
| 859 // like extensions removing the "report abuse" link). | 830 // like extensions removing the "report abuse" link). |
| 860 // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing | 831 // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1151 } | 1122 } |
| 1152 | 1123 |
| 1153 bool Extension::can_be_incognito_enabled() const { | 1124 bool Extension::can_be_incognito_enabled() const { |
| 1154 return !is_platform_app(); | 1125 return !is_platform_app(); |
| 1155 } | 1126 } |
| 1156 | 1127 |
| 1157 void Extension::AddWebExtentPattern(const URLPattern& pattern) { | 1128 void Extension::AddWebExtentPattern(const URLPattern& pattern) { |
| 1158 extent_.AddPattern(pattern); | 1129 extent_.AddPattern(pattern); |
| 1159 } | 1130 } |
| 1160 | 1131 |
| 1132 void Extension::ClearWebExtentPatterns() { |
| 1133 extent_.ClearPatterns(); |
| 1134 } |
| 1135 |
| 1161 bool Extension::is_theme() const { | 1136 bool Extension::is_theme() const { |
| 1162 return manifest()->is_theme(); | 1137 return manifest()->is_theme(); |
| 1163 } | 1138 } |
| 1164 | 1139 |
| 1165 bool Extension::is_content_pack() const { | 1140 bool Extension::is_content_pack() const { |
| 1166 return !content_pack_site_list_.empty(); | 1141 return !content_pack_site_list_.empty(); |
| 1167 } | 1142 } |
| 1168 | 1143 |
| 1169 ExtensionResource Extension::GetContentPackSiteList() const { | 1144 ExtensionResource Extension::GetContentPackSiteList() const { |
| 1170 if (!is_content_pack()) | 1145 if (!is_content_pack()) |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1329 scoped_ptr<extensions::Manifest> manifest) | 1304 scoped_ptr<extensions::Manifest> manifest) |
| 1330 : manifest_version_(0), | 1305 : manifest_version_(0), |
| 1331 incognito_split_mode_(false), | 1306 incognito_split_mode_(false), |
| 1332 offline_enabled_(false), | 1307 offline_enabled_(false), |
| 1333 converted_from_user_script_(false), | 1308 converted_from_user_script_(false), |
| 1334 background_page_is_persistent_(true), | 1309 background_page_is_persistent_(true), |
| 1335 allow_background_js_access_(true), | 1310 allow_background_js_access_(true), |
| 1336 manifest_(manifest.release()), | 1311 manifest_(manifest.release()), |
| 1337 finished_parsing_manifest_(false), | 1312 finished_parsing_manifest_(false), |
| 1338 is_storage_isolated_(false), | 1313 is_storage_isolated_(false), |
| 1339 launch_container_(extension_misc::LAUNCH_TAB), | |
| 1340 launch_width_(0), | |
| 1341 launch_height_(0), | |
| 1342 display_in_launcher_(true), | 1314 display_in_launcher_(true), |
| 1343 display_in_new_tab_page_(true), | 1315 display_in_new_tab_page_(true), |
| 1344 wants_file_access_(false), | 1316 wants_file_access_(false), |
| 1345 creation_flags_(0) { | 1317 creation_flags_(0) { |
| 1346 DCHECK(path.empty() || path.IsAbsolute()); | 1318 DCHECK(path.empty() || path.IsAbsolute()); |
| 1347 path_ = MaybeNormalizePath(path); | 1319 path_ = MaybeNormalizePath(path); |
| 1348 } | 1320 } |
| 1349 | 1321 |
| 1350 Extension::~Extension() { | 1322 Extension::~Extension() { |
| 1351 } | 1323 } |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1528 version_.reset(new Version(version_str)); | 1500 version_.reset(new Version(version_str)); |
| 1529 if (!version_->IsValid() || version_->components().size() > 4) { | 1501 if (!version_->IsValid() || version_->components().size() > 4) { |
| 1530 *error = ASCIIToUTF16(errors::kInvalidVersion); | 1502 *error = ASCIIToUTF16(errors::kInvalidVersion); |
| 1531 return false; | 1503 return false; |
| 1532 } | 1504 } |
| 1533 return true; | 1505 return true; |
| 1534 } | 1506 } |
| 1535 | 1507 |
| 1536 bool Extension::LoadAppFeatures(string16* error) { | 1508 bool Extension::LoadAppFeatures(string16* error) { |
| 1537 if (!LoadExtent(keys::kWebURLs, &extent_, | 1509 if (!LoadExtent(keys::kWebURLs, &extent_, |
| 1538 errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || | 1510 errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) { |
| 1539 !LoadLaunchURL(error) || | |
| 1540 !LoadLaunchContainer(error)) { | |
| 1541 return false; | 1511 return false; |
| 1542 } | 1512 } |
| 1543 if (manifest_->HasKey(keys::kDisplayInLauncher) && | 1513 if (manifest_->HasKey(keys::kDisplayInLauncher) && |
| 1544 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { | 1514 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { |
| 1545 *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher); | 1515 *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher); |
| 1546 return false; | 1516 return false; |
| 1547 } | 1517 } |
| 1548 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { | 1518 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { |
| 1549 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, | 1519 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, |
| 1550 &display_in_new_tab_page_)) { | 1520 &display_in_new_tab_page_)) { |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1625 return false; | 1595 return false; |
| 1626 } | 1596 } |
| 1627 pattern.SetPath(pattern.path() + '*'); | 1597 pattern.SetPath(pattern.path() + '*'); |
| 1628 | 1598 |
| 1629 extent->AddPattern(pattern); | 1599 extent->AddPattern(pattern); |
| 1630 } | 1600 } |
| 1631 | 1601 |
| 1632 return true; | 1602 return true; |
| 1633 } | 1603 } |
| 1634 | 1604 |
| 1635 bool Extension::LoadLaunchContainer(string16* error) { | |
| 1636 Value* tmp_launcher_container = NULL; | |
| 1637 if (!manifest_->Get(keys::kLaunchContainer, &tmp_launcher_container)) | |
| 1638 return true; | |
| 1639 | |
| 1640 std::string launch_container_string; | |
| 1641 if (!tmp_launcher_container->GetAsString(&launch_container_string)) { | |
| 1642 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
| 1643 return false; | |
| 1644 } | |
| 1645 | |
| 1646 if (launch_container_string == values::kLaunchContainerPanel) { | |
| 1647 launch_container_ = extension_misc::LAUNCH_PANEL; | |
| 1648 } else if (launch_container_string == values::kLaunchContainerTab) { | |
| 1649 launch_container_ = extension_misc::LAUNCH_TAB; | |
| 1650 } else { | |
| 1651 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
| 1652 return false; | |
| 1653 } | |
| 1654 | |
| 1655 bool can_specify_initial_size = | |
| 1656 launch_container_ == extension_misc::LAUNCH_PANEL || | |
| 1657 launch_container_ == extension_misc::LAUNCH_WINDOW; | |
| 1658 | |
| 1659 // Validate the container width if present. | |
| 1660 if (!ReadLaunchDimension(manifest_.get(), | |
| 1661 keys::kLaunchWidth, | |
| 1662 &launch_width_, | |
| 1663 can_specify_initial_size, | |
| 1664 error)) { | |
| 1665 return false; | |
| 1666 } | |
| 1667 | |
| 1668 // Validate container height if present. | |
| 1669 if (!ReadLaunchDimension(manifest_.get(), | |
| 1670 keys::kLaunchHeight, | |
| 1671 &launch_height_, | |
| 1672 can_specify_initial_size, | |
| 1673 error)) { | |
| 1674 return false; | |
| 1675 } | |
| 1676 | |
| 1677 return true; | |
| 1678 } | |
| 1679 | |
| 1680 bool Extension::LoadLaunchURL(string16* error) { | |
| 1681 Value* temp = NULL; | |
| 1682 | |
| 1683 // launch URL can be either local (to chrome-extension:// root) or an absolute | |
| 1684 // web URL. | |
| 1685 if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { | |
| 1686 if (manifest_->Get(keys::kLaunchWebURL, NULL)) { | |
| 1687 *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); | |
| 1688 return false; | |
| 1689 } | |
| 1690 | |
| 1691 if (manifest_->Get(keys::kWebURLs, NULL)) { | |
| 1692 *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive); | |
| 1693 return false; | |
| 1694 } | |
| 1695 | |
| 1696 std::string launch_path; | |
| 1697 if (!temp->GetAsString(&launch_path)) { | |
| 1698 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 1699 errors::kInvalidLaunchValue, | |
| 1700 keys::kLaunchLocalPath); | |
| 1701 return false; | |
| 1702 } | |
| 1703 | |
| 1704 // Ensure the launch path is a valid relative URL. | |
| 1705 GURL resolved = url().Resolve(launch_path); | |
| 1706 if (!resolved.is_valid() || resolved.GetOrigin() != url()) { | |
| 1707 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 1708 errors::kInvalidLaunchValue, | |
| 1709 keys::kLaunchLocalPath); | |
| 1710 return false; | |
| 1711 } | |
| 1712 | |
| 1713 launch_local_path_ = launch_path; | |
| 1714 } else if (manifest_->Get(keys::kLaunchWebURL, &temp)) { | |
| 1715 std::string launch_url; | |
| 1716 if (!temp->GetAsString(&launch_url)) { | |
| 1717 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 1718 errors::kInvalidLaunchValue, | |
| 1719 keys::kLaunchWebURL); | |
| 1720 return false; | |
| 1721 } | |
| 1722 | |
| 1723 // Ensure the launch URL is a valid absolute URL and web extent scheme. | |
| 1724 GURL url(launch_url); | |
| 1725 URLPattern pattern(kValidWebExtentSchemes); | |
| 1726 if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { | |
| 1727 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 1728 errors::kInvalidLaunchValue, | |
| 1729 keys::kLaunchWebURL); | |
| 1730 return false; | |
| 1731 } | |
| 1732 | |
| 1733 launch_web_url_ = launch_url; | |
| 1734 } else if (is_legacy_packaged_app() || is_hosted_app()) { | |
| 1735 *error = ASCIIToUTF16(errors::kLaunchURLRequired); | |
| 1736 return false; | |
| 1737 } | |
| 1738 | |
| 1739 // If there is no extent, we default the extent based on the launch URL. | |
| 1740 if (web_extent().is_empty() && !launch_web_url().empty()) { | |
| 1741 GURL launch_url(launch_web_url()); | |
| 1742 URLPattern pattern(kValidWebExtentSchemes); | |
| 1743 if (!pattern.SetScheme("*")) { | |
| 1744 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 1745 errors::kInvalidLaunchValue, | |
| 1746 keys::kLaunchWebURL); | |
| 1747 return false; | |
| 1748 } | |
| 1749 pattern.SetHost(launch_url.host()); | |
| 1750 pattern.SetPath("/*"); | |
| 1751 extent_.AddPattern(pattern); | |
| 1752 } | |
| 1753 | |
| 1754 // In order for the --apps-gallery-url switch to work with the gallery | |
| 1755 // process isolation, we must insert any provided value into the component | |
| 1756 // app's launch url and web extent. | |
| 1757 if (id() == extension_misc::kWebStoreAppId) { | |
| 1758 std::string gallery_url_str = CommandLine::ForCurrentProcess()-> | |
| 1759 GetSwitchValueASCII(switches::kAppsGalleryURL); | |
| 1760 | |
| 1761 // Empty string means option was not used. | |
| 1762 if (!gallery_url_str.empty()) { | |
| 1763 GURL gallery_url(gallery_url_str); | |
| 1764 OverrideLaunchUrl(gallery_url); | |
| 1765 } | |
| 1766 } else if (id() == extension_misc::kCloudPrintAppId) { | |
| 1767 // In order for the --cloud-print-service switch to work, we must update | |
| 1768 // the launch URL and web extent. | |
| 1769 // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is | |
| 1770 // currently under chrome/browser. | |
| 1771 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 1772 GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII( | |
| 1773 switches::kCloudPrintServiceURL)); | |
| 1774 if (!cloud_print_service_url.is_empty()) { | |
| 1775 std::string path( | |
| 1776 cloud_print_service_url.path() + "/enable_chrome_connector"); | |
| 1777 GURL::Replacements replacements; | |
| 1778 replacements.SetPathStr(path); | |
| 1779 GURL cloud_print_enable_connector_url = | |
| 1780 cloud_print_service_url.ReplaceComponents(replacements); | |
| 1781 OverrideLaunchUrl(cloud_print_enable_connector_url); | |
| 1782 } | |
| 1783 } else if (id() == extension_misc::kChromeAppId) { | |
| 1784 // Override launch url to new tab. | |
| 1785 launch_web_url_ = chrome::kChromeUINewTabURL; | |
| 1786 extent_.ClearPatterns(); | |
| 1787 } | |
| 1788 | |
| 1789 return true; | |
| 1790 } | |
| 1791 | |
| 1792 bool Extension::LoadSharedFeatures(string16* error) { | 1605 bool Extension::LoadSharedFeatures(string16* error) { |
| 1793 if (!LoadDescription(error) || | 1606 if (!LoadDescription(error) || |
| 1794 !LoadIcons(error) || | 1607 !LoadIcons(error) || |
| 1795 !ManifestHandler::ParseExtension(this, error) || | 1608 !ManifestHandler::ParseExtension(this, error) || |
| 1796 !LoadPlugins(error) || | 1609 !LoadPlugins(error) || |
| 1797 !LoadNaClModules(error) || | 1610 !LoadNaClModules(error) || |
| 1798 !LoadSandboxedPages(error) || | 1611 !LoadSandboxedPages(error) || |
| 1799 !LoadRequirements(error) || | 1612 !LoadRequirements(error) || |
| 1800 !LoadOfflineEnabled(error) || | 1613 !LoadOfflineEnabled(error) || |
| 1801 // LoadBackgroundScripts() must be called before LoadBackgroundPage(). | 1614 // LoadBackgroundScripts() must be called before LoadBackgroundPage(). |
| (...skipping 764 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2566 | 2379 |
| 2567 if (ActionInfo::GetBrowserActionInfo(this)) | 2380 if (ActionInfo::GetBrowserActionInfo(this)) |
| 2568 ++num_surfaces; | 2381 ++num_surfaces; |
| 2569 | 2382 |
| 2570 if (is_app()) | 2383 if (is_app()) |
| 2571 ++num_surfaces; | 2384 ++num_surfaces; |
| 2572 | 2385 |
| 2573 return num_surfaces > 1; | 2386 return num_surfaces > 1; |
| 2574 } | 2387 } |
| 2575 | 2388 |
| 2576 void Extension::OverrideLaunchUrl(const GURL& override_url) { | |
| 2577 GURL new_url(override_url); | |
| 2578 if (!new_url.is_valid()) { | |
| 2579 DLOG(WARNING) << "Invalid override url given for " << name(); | |
| 2580 } else { | |
| 2581 if (new_url.has_port()) { | |
| 2582 DLOG(WARNING) << "Override URL passed for " << name() | |
| 2583 << " should not contain a port. Removing it."; | |
| 2584 | |
| 2585 GURL::Replacements remove_port; | |
| 2586 remove_port.ClearPort(); | |
| 2587 new_url = new_url.ReplaceComponents(remove_port); | |
| 2588 } | |
| 2589 | |
| 2590 launch_web_url_ = new_url.spec(); | |
| 2591 | |
| 2592 URLPattern pattern(kValidWebExtentSchemes); | |
| 2593 URLPattern::ParseResult result = pattern.Parse(new_url.spec()); | |
| 2594 DCHECK_EQ(result, URLPattern::PARSE_SUCCESS); | |
| 2595 pattern.SetPath(pattern.path() + '*'); | |
| 2596 extent_.AddPattern(pattern); | |
| 2597 } | |
| 2598 } | |
| 2599 | |
| 2600 bool Extension::CanSpecifyExperimentalPermission() const { | 2389 bool Extension::CanSpecifyExperimentalPermission() const { |
| 2601 if (location() == Manifest::COMPONENT) | 2390 if (location() == Manifest::COMPONENT) |
| 2602 return true; | 2391 return true; |
| 2603 | 2392 |
| 2604 if (CommandLine::ForCurrentProcess()->HasSwitch( | 2393 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 2605 switches::kEnableExperimentalExtensionApis)) { | 2394 switches::kEnableExperimentalExtensionApis)) { |
| 2606 return true; | 2395 return true; |
| 2607 } | 2396 } |
| 2608 | 2397 |
| 2609 // We rely on the webstore to check access to experimental. This way we can | 2398 // We rely on the webstore to check access to experimental. This way we can |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2727 | 2516 |
| 2728 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( | 2517 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( |
| 2729 const Extension* extension, | 2518 const Extension* extension, |
| 2730 const PermissionSet* permissions, | 2519 const PermissionSet* permissions, |
| 2731 Reason reason) | 2520 Reason reason) |
| 2732 : reason(reason), | 2521 : reason(reason), |
| 2733 extension(extension), | 2522 extension(extension), |
| 2734 permissions(permissions) {} | 2523 permissions(permissions) {} |
| 2735 | 2524 |
| 2736 } // namespace extensions | 2525 } // namespace extensions |
| OLD | NEW |