Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/base64.h" | 9 #include "base/base64.h" |
| 10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/file_path.h" | 12 #include "base/file_path.h" |
| 13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
| 14 #include "base/i18n/rtl.h" | 14 #include "base/i18n/rtl.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/memory/singleton.h" | 16 #include "base/memory/singleton.h" |
| 17 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
| 18 #include "base/string16.h" | 18 #include "base/string16.h" |
| 19 #include "base/string_number_conversions.h" | 19 #include "base/string_number_conversions.h" |
| 20 #include "base/string_piece.h" | 20 #include "base/string_piece.h" |
| 21 #include "base/string_split.h" | |
| 21 #include "base/string_util.h" | 22 #include "base/string_util.h" |
| 22 #include "base/utf_string_conversions.h" | 23 #include "base/utf_string_conversions.h" |
| 23 #include "base/values.h" | 24 #include "base/values.h" |
| 24 #include "base/version.h" | 25 #include "base/version.h" |
| 25 #include "crypto/sha2.h" | 26 #include "crypto/sha2.h" |
| 26 #include "chrome/common/chrome_constants.h" | 27 #include "chrome/common/chrome_constants.h" |
| 27 #include "chrome/common/chrome_switches.h" | 28 #include "chrome/common/chrome_switches.h" |
| 28 #include "chrome/common/chrome_version_info.h" | 29 #include "chrome/common/chrome_version_info.h" |
| 29 #include "chrome/common/extensions/csp_validator.h" | 30 #include "chrome/common/extensions/csp_validator.h" |
| 30 #include "chrome/common/extensions/extension_action.h" | 31 #include "chrome/common/extensions/extension_action.h" |
| 31 #include "chrome/common/extensions/extension_constants.h" | 32 #include "chrome/common/extensions/extension_constants.h" |
| 32 #include "chrome/common/extensions/extension_error_utils.h" | 33 #include "chrome/common/extensions/extension_error_utils.h" |
| 33 #include "chrome/common/extensions/extension_l10n_util.h" | 34 #include "chrome/common/extensions/extension_l10n_util.h" |
| 34 #include "chrome/common/extensions/extension_resource.h" | 35 #include "chrome/common/extensions/extension_resource.h" |
| 35 #include "chrome/common/extensions/file_browser_handler.h" | 36 #include "chrome/common/extensions/file_browser_handler.h" |
| 36 #include "chrome/common/extensions/manifest.h" | 37 #include "chrome/common/extensions/manifest.h" |
| 37 #include "chrome/common/extensions/user_script.h" | 38 #include "chrome/common/extensions/user_script.h" |
| 38 #include "chrome/common/url_constants.h" | 39 #include "chrome/common/url_constants.h" |
| 39 #include "googleurl/src/url_util.h" | 40 #include "googleurl/src/url_util.h" |
| 40 #include "grit/chromium_strings.h" | 41 #include "grit/chromium_strings.h" |
| 41 #include "grit/generated_resources.h" | 42 #include "grit/generated_resources.h" |
| 42 #include "grit/theme_resources.h" | 43 #include "grit/theme_resources.h" |
| 43 #include "net/base/registry_controlled_domain.h" | 44 #include "net/base/registry_controlled_domain.h" |
| 44 #include "third_party/skia/include/core/SkBitmap.h" | 45 #include "third_party/skia/include/core/SkBitmap.h" |
| 46 #include "ui/base/keycodes/keyboard_codes.h" | |
| 45 #include "ui/base/l10n/l10n_util.h" | 47 #include "ui/base/l10n/l10n_util.h" |
| 46 #include "ui/base/resource/resource_bundle.h" | 48 #include "ui/base/resource/resource_bundle.h" |
| 47 #include "webkit/glue/image_decoder.h" | 49 #include "webkit/glue/image_decoder.h" |
| 48 #include "webkit/glue/web_intent_service_data.h" | 50 #include "webkit/glue/web_intent_service_data.h" |
| 49 | 51 |
| 50 namespace keys = extension_manifest_keys; | 52 namespace keys = extension_manifest_keys; |
| 51 namespace values = extension_manifest_values; | 53 namespace values = extension_manifest_values; |
| 52 namespace errors = extension_manifest_errors; | 54 namespace errors = extension_manifest_errors; |
| 53 | 55 |
| 54 using extensions::csp_validator::ContentSecurityPolicyIsLegal; | 56 using extensions::csp_validator::ContentSecurityPolicyIsLegal; |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 212 shortcut_alt(false), | 214 shortcut_alt(false), |
| 213 shortcut_ctrl(false), | 215 shortcut_ctrl(false), |
| 214 shortcut_shift(false) { | 216 shortcut_shift(false) { |
| 215 } | 217 } |
| 216 | 218 |
| 217 Extension::InputComponentInfo::~InputComponentInfo() {} | 219 Extension::InputComponentInfo::~InputComponentInfo() {} |
| 218 | 220 |
| 219 Extension::TtsVoice::TtsVoice() {} | 221 Extension::TtsVoice::TtsVoice() {} |
| 220 Extension::TtsVoice::~TtsVoice() {} | 222 Extension::TtsVoice::~TtsVoice() {} |
| 221 | 223 |
| 224 Extension::ExtensionKeybinding::ExtensionKeybinding() {} | |
| 225 Extension::ExtensionKeybinding::~ExtensionKeybinding() {} | |
| 226 | |
| 227 bool Extension::ExtensionKeybinding::Parse(DictionaryValue* command, | |
| 228 const std::string& command_name, | |
| 229 int index, | |
| 230 string16* error) { | |
| 231 DCHECK(!command_name.empty()); | |
| 232 std::string key_binding; | |
| 233 if (!command->GetString(keys::kKey, &key_binding) || | |
| 234 key_binding.empty()) { | |
| 235 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 236 errors::kInvalidKeyBinding, | |
| 237 base::IntToString(index), | |
| 238 "Missing"); | |
| 239 return false; | |
| 240 } | |
| 241 | |
| 242 std::string original_keybinding = key_binding; | |
| 243 // Normalize '-' to '+'. | |
| 244 ReplaceSubstringsAfterOffset(&key_binding, 0, "-", "+"); | |
| 245 // Remove all spaces. | |
| 246 ReplaceSubstringsAfterOffset(&key_binding, 0, " ", ""); | |
| 247 // And finally, lower-case it. | |
| 248 key_binding = StringToLowerASCII(key_binding); | |
| 249 | |
| 250 std::vector<std::string> tokens; | |
| 251 base::SplitString(key_binding, '+', &tokens); | |
| 252 if (tokens.size() < 2 || tokens.size() > 3) { | |
| 253 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 254 errors::kInvalidKeyBinding, | |
| 255 base::IntToString(index), | |
| 256 original_keybinding); | |
| 257 return false; | |
| 258 } | |
| 259 | |
| 260 // Now, parse it into an accelerator. | |
| 261 bool ctrl = false; | |
| 262 bool alt = false; | |
| 263 bool shift = false; | |
| 264 ui::KeyboardCode key = ui::VKEY_UNKNOWN; | |
| 265 for (size_t i = 0; i < tokens.size(); i++) { | |
| 266 if (tokens[i] == "ctrl") { | |
| 267 ctrl = true; | |
| 268 } else if (tokens[i] == "alt") { | |
| 269 alt = true; | |
| 270 } else if (tokens[i] == "shift") { | |
| 271 shift = true; | |
| 272 } else if (tokens[i].size() == 1 && | |
| 273 base::ToUpperASCII(tokens[i][0]) >= ui::VKEY_A && | |
|
Matt Perry
2012/02/22 23:59:42
Just use 'a' so you don't have to uppercase it. Al
| |
| 274 base::ToUpperASCII(tokens[i][0]) <= ui::VKEY_Z) { | |
| 275 if (key != ui::VKEY_UNKNOWN) { | |
| 276 // Multiple key assignments. | |
| 277 key = ui::VKEY_UNKNOWN; | |
| 278 break; | |
| 279 } | |
| 280 | |
| 281 key = static_cast<ui::KeyboardCode>( | |
| 282 ui::VKEY_A + (base::ToUpperASCII(tokens[i][0]) - ui::VKEY_A)); | |
|
Matt Perry
2012/02/22 23:59:42
see above, second VKEY_A => 'a'
| |
| 283 } else { | |
| 284 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 285 errors::kInvalidKeyBinding, | |
| 286 base::IntToString(index), | |
| 287 original_keybinding); | |
| 288 return false; | |
| 289 } | |
| 290 } | |
| 291 | |
| 292 // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not | |
| 293 // Ctrl+Alt+foo. For a more detailed reason why we don't support Ctrl+Alt+foo: | |
| 294 // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx. | |
| 295 if (key == ui::VKEY_UNKNOWN || ctrl == true && alt == true) { | |
|
Matt Perry
2012/02/22 23:59:42
Please add parens around (ctrl == true && alt == t
| |
| 296 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 297 errors::kInvalidKeyBinding, | |
| 298 base::IntToString(index), | |
| 299 original_keybinding); | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 accelerator_ = ui::Accelerator(key, shift, ctrl, alt); | |
| 304 | |
| 305 if (command_name != | |
| 306 extension_manifest_values::kPageActionKeybindingEvent && | |
| 307 command_name != | |
| 308 extension_manifest_values::kBrowserActionKeybindingEvent) { | |
| 309 if (!command->GetString(keys::kDescription, &description_) || | |
| 310 description_.empty()) { | |
| 311 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 312 errors::kInvalidKeyBindingDescription, | |
| 313 base::IntToString(index)); | |
| 314 return false; | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 command_name_ = command_name; | |
| 319 return true; | |
| 320 } | |
| 321 | |
| 222 // | 322 // |
| 223 // Extension | 323 // Extension |
| 224 // | 324 // |
| 225 | 325 |
| 226 // static | 326 // static |
| 227 scoped_refptr<Extension> Extension::Create(const FilePath& path, | 327 scoped_refptr<Extension> Extension::Create(const FilePath& path, |
| 228 Location location, | 328 Location location, |
| 229 const DictionaryValue& value, | 329 const DictionaryValue& value, |
| 230 int flags, | 330 int flags, |
| 231 std::string* utf8_error) { | 331 std::string* utf8_error) { |
| (...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 851 errors::kInvalidURLPatternError, filter); | 951 errors::kInvalidURLPatternError, filter); |
| 852 return NULL; | 952 return NULL; |
| 853 } | 953 } |
| 854 result->AddPattern(pattern); | 954 result->AddPattern(pattern); |
| 855 } | 955 } |
| 856 | 956 |
| 857 std::string default_icon; | 957 std::string default_icon; |
| 858 // Read the file browser action |default_icon| (optional). | 958 // Read the file browser action |default_icon| (optional). |
| 859 if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) { | 959 if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) { |
| 860 if (!file_browser_handler->GetString( | 960 if (!file_browser_handler->GetString( |
| 861 keys::kPageActionDefaultIcon,&default_icon) || | 961 keys::kPageActionDefaultIcon, &default_icon) || |
| 862 default_icon.empty()) { | 962 default_icon.empty()) { |
| 863 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); | 963 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
| 864 return NULL; | 964 return NULL; |
| 865 } | 965 } |
| 866 result->set_icon_path(default_icon); | 966 result->set_icon_path(default_icon); |
| 867 } | 967 } |
| 868 | 968 |
| 869 return result.release(); | 969 return result.release(); |
| 870 } | 970 } |
| 871 | 971 |
| (...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1155 *error = ASCIIToUTF16(errors::kInvalidIntent); | 1255 *error = ASCIIToUTF16(errors::kInvalidIntent); |
| 1156 return false; | 1256 return false; |
| 1157 } | 1257 } |
| 1158 service.action = UTF8ToUTF16(*iter); | 1258 service.action = UTF8ToUTF16(*iter); |
| 1159 | 1259 |
| 1160 ListValue* mime_types = NULL; | 1260 ListValue* mime_types = NULL; |
| 1161 if (!one_service->HasKey(keys::kIntentType) || | 1261 if (!one_service->HasKey(keys::kIntentType) || |
| 1162 !one_service->GetList(keys::kIntentType, &mime_types) || | 1262 !one_service->GetList(keys::kIntentType, &mime_types) || |
| 1163 mime_types->GetSize() == 0) { | 1263 mime_types->GetSize() == 0) { |
| 1164 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1264 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| 1165 errors::kInvalidIntentType,*iter); | 1265 errors::kInvalidIntentType, *iter); |
| 1166 return false; | 1266 return false; |
| 1167 } | 1267 } |
| 1168 | 1268 |
| 1169 if (one_service->HasKey(keys::kIntentPath)) { | 1269 if (one_service->HasKey(keys::kIntentPath)) { |
| 1170 if (!one_service->GetString(keys::kIntentPath, &value)) { | 1270 if (!one_service->GetString(keys::kIntentPath, &value)) { |
| 1171 *error = ASCIIToUTF16(errors::kInvalidIntentPath); | 1271 *error = ASCIIToUTF16(errors::kInvalidIntentPath); |
| 1172 return false; | 1272 return false; |
| 1173 } | 1273 } |
| 1174 if (is_hosted_app()) { | 1274 if (is_hosted_app()) { |
| 1175 // Hosted apps require an absolute URL for intents. | 1275 // Hosted apps require an absolute URL for intents. |
| 1176 GURL service_url(value); | 1276 GURL service_url(value); |
| 1177 if (!service_url.is_valid() || | 1277 if (!service_url.is_valid() || |
| 1178 !(web_extent().MatchesURL(service_url))) { | 1278 !(web_extent().MatchesURL(service_url))) { |
| 1179 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1279 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| 1180 errors::kInvalidIntentPageInHostedApp,*iter); | 1280 errors::kInvalidIntentPageInHostedApp, *iter); |
| 1181 return false; | 1281 return false; |
| 1182 } | 1282 } |
| 1183 service.service_url = service_url; | 1283 service.service_url = service_url; |
| 1184 } else { | 1284 } else { |
| 1185 // We do not allow absolute intent URLs in non-hosted apps. | 1285 // We do not allow absolute intent URLs in non-hosted apps. |
| 1186 if (GURL(value).is_valid()) { | 1286 if (GURL(value).is_valid()) { |
| 1187 *error =ExtensionErrorUtils::FormatErrorMessageUTF16( | 1287 *error =ExtensionErrorUtils::FormatErrorMessageUTF16( |
| 1188 errors::kCannotAccessPage,value.c_str()); | 1288 errors::kCannotAccessPage, value.c_str()); |
| 1189 return false; | 1289 return false; |
| 1190 } | 1290 } |
| 1191 service.service_url = GetResourceURL(value); | 1291 service.service_url = GetResourceURL(value); |
| 1192 } | 1292 } |
| 1193 } | 1293 } |
| 1194 | 1294 |
| 1195 if (one_service->HasKey(keys::kIntentTitle) && | 1295 if (one_service->HasKey(keys::kIntentTitle) && |
| 1196 !one_service->GetString(keys::kIntentTitle, &service.title)) { | 1296 !one_service->GetString(keys::kIntentTitle, &service.title)) { |
| 1197 *error = ASCIIToUTF16(errors::kInvalidIntentTitle); | 1297 *error = ASCIIToUTF16(errors::kInvalidIntentTitle); |
| 1198 return false; | 1298 return false; |
| (...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1712 minimum_version_string); | 1812 minimum_version_string); |
| 1713 return false; | 1813 return false; |
| 1714 } | 1814 } |
| 1715 } | 1815 } |
| 1716 | 1816 |
| 1717 // Initialize converted_from_user_script (if present) | 1817 // Initialize converted_from_user_script (if present) |
| 1718 if (manifest->HasKey(keys::kConvertedFromUserScript)) | 1818 if (manifest->HasKey(keys::kConvertedFromUserScript)) |
| 1719 manifest->GetBoolean(keys::kConvertedFromUserScript, | 1819 manifest->GetBoolean(keys::kConvertedFromUserScript, |
| 1720 &converted_from_user_script_); | 1820 &converted_from_user_script_); |
| 1721 | 1821 |
| 1822 // Initialize commands (if present). | |
| 1823 if (manifest->HasKey(keys::kCommands)) { | |
| 1824 DictionaryValue* commands = NULL; | |
| 1825 if (!manifest->GetDictionary(keys::kCommands, &commands)) { | |
| 1826 *error = ASCIIToUTF16(errors::kInvalidCommandsKey); | |
| 1827 return false; | |
| 1828 } | |
| 1829 | |
| 1830 int command_index = 0; | |
| 1831 for (DictionaryValue::key_iterator iter = commands->begin_keys(); | |
| 1832 iter != commands->end_keys(); ++iter) { | |
| 1833 ++command_index; | |
| 1834 | |
| 1835 DictionaryValue* command = NULL; | |
| 1836 if (!commands->GetDictionary(*iter, &command)) { | |
| 1837 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1838 errors::kInvalidKeyBindingDictionary, | |
| 1839 base::IntToString(command_index)); | |
| 1840 return false; | |
| 1841 } | |
| 1842 | |
| 1843 ExtensionKeybinding binding; | |
| 1844 if (!binding.Parse(command, *iter, command_index, error)) | |
| 1845 return false; // |error| already set. | |
| 1846 | |
| 1847 commands_.push_back(binding); | |
| 1848 } | |
| 1849 } | |
| 1850 | |
| 1722 // Initialize icons (if present). | 1851 // Initialize icons (if present). |
| 1723 if (manifest->HasKey(keys::kIcons)) { | 1852 if (manifest->HasKey(keys::kIcons)) { |
| 1724 DictionaryValue* icons_value = NULL; | 1853 DictionaryValue* icons_value = NULL; |
| 1725 if (!manifest->GetDictionary(keys::kIcons, &icons_value)) { | 1854 if (!manifest->GetDictionary(keys::kIcons, &icons_value)) { |
| 1726 *error = ASCIIToUTF16(errors::kInvalidIcons); | 1855 *error = ASCIIToUTF16(errors::kInvalidIcons); |
| 1727 return false; | 1856 return false; |
| 1728 } | 1857 } |
| 1729 | 1858 |
| 1730 for (size_t i = 0; i < arraysize(kIconSizes); ++i) { | 1859 for (size_t i = 0; i < arraysize(kIconSizes); ++i) { |
| 1731 std::string key = base::IntToString(kIconSizes[i]); | 1860 std::string key = base::IntToString(kIconSizes[i]); |
| (...skipping 1143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2875 | 3004 |
| 2876 if (permission->id() == ExtensionAPIPermission::kExperimental) { | 3005 if (permission->id() == ExtensionAPIPermission::kExperimental) { |
| 2877 if (!CanSpecifyExperimentalPermission()) { | 3006 if (!CanSpecifyExperimentalPermission()) { |
| 2878 *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); | 3007 *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); |
| 2879 return false; | 3008 return false; |
| 2880 } | 3009 } |
| 2881 } | 3010 } |
| 2882 | 3011 |
| 2883 bool supports_type = false; | 3012 bool supports_type = false; |
| 2884 switch (GetType()) { | 3013 switch (GetType()) { |
| 2885 case TYPE_USER_SCRIPT: // Pass through. | 3014 case TYPE_USER_SCRIPT: // Pass through. |
| 2886 case TYPE_EXTENSION: | 3015 case TYPE_EXTENSION: |
| 2887 supports_type = permission->supports_extensions(); | 3016 supports_type = permission->supports_extensions(); |
| 2888 break; | 3017 break; |
| 2889 case TYPE_HOSTED_APP: | 3018 case TYPE_HOSTED_APP: |
| 2890 supports_type = permission->supports_hosted_apps(); | 3019 supports_type = permission->supports_hosted_apps(); |
| 2891 break; | 3020 break; |
| 2892 case TYPE_PACKAGED_APP: | 3021 case TYPE_PACKAGED_APP: |
| 2893 supports_type = permission->supports_packaged_apps(); | 3022 supports_type = permission->supports_packaged_apps(); |
| 2894 break; | 3023 break; |
| 2895 case TYPE_PLATFORM_APP: | 3024 case TYPE_PLATFORM_APP: |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3031 } | 3160 } |
| 3032 } | 3161 } |
| 3033 | 3162 |
| 3034 bool Extension::IsSyncable() const { | 3163 bool Extension::IsSyncable() const { |
| 3035 // TODO(akalin): Figure out if we need to allow some other types. | 3164 // TODO(akalin): Figure out if we need to allow some other types. |
| 3036 | 3165 |
| 3037 // We want to sync any extensions that are shown in the luancher because | 3166 // We want to sync any extensions that are shown in the luancher because |
| 3038 // their positions should sync. | 3167 // their positions should sync. |
| 3039 return location_ == Extension::INTERNAL || | 3168 return location_ == Extension::INTERNAL || |
| 3040 ShouldDisplayInLauncher(); | 3169 ShouldDisplayInLauncher(); |
| 3041 | |
| 3042 } | 3170 } |
| 3043 | 3171 |
| 3044 bool Extension::ShouldDisplayInLauncher() const { | 3172 bool Extension::ShouldDisplayInLauncher() const { |
| 3045 // All apps should be displayed on the NTP except for the Cloud Print App. | 3173 // All apps should be displayed on the NTP except for the Cloud Print App. |
| 3046 return is_app() && id() != extension_misc::kCloudPrintAppId; | 3174 return is_app() && id() != extension_misc::kCloudPrintAppId; |
| 3047 } | 3175 } |
| 3048 | 3176 |
| 3049 ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, | 3177 ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, |
| 3050 const std::string& id, | 3178 const std::string& id, |
| 3051 const FilePath& path, | 3179 const FilePath& path, |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 3081 already_disabled(false), | 3209 already_disabled(false), |
| 3082 extension(extension) {} | 3210 extension(extension) {} |
| 3083 | 3211 |
| 3084 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( | 3212 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( |
| 3085 const Extension* extension, | 3213 const Extension* extension, |
| 3086 const ExtensionPermissionSet* permissions, | 3214 const ExtensionPermissionSet* permissions, |
| 3087 Reason reason) | 3215 Reason reason) |
| 3088 : reason(reason), | 3216 : reason(reason), |
| 3089 extension(extension), | 3217 extension(extension), |
| 3090 permissions(permissions) {} | 3218 permissions(permissions) {} |
| OLD | NEW |