diff options
author | elijahtaylor@chromium.org <elijahtaylor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-30 01:04:35 +0000 |
---|---|---|
committer | elijahtaylor@chromium.org <elijahtaylor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-30 01:04:35 +0000 |
commit | b56d3c7fc3e61736747d63aac703bab4a51df8db (patch) | |
tree | 16bc7b6de6144cf8b633c7b3f5d0982fc1c79b1e | |
parent | aa249b505342a9e1203440dcd04b94901fbf8f80 (diff) | |
download | chromium_src-b56d3c7fc3e61736747d63aac703bab4a51df8db.zip chromium_src-b56d3c7fc3e61736747d63aac703bab4a51df8db.tar.gz chromium_src-b56d3c7fc3e61736747d63aac703bab4a51df8db.tar.bz2 |
Basic multi-module support
Allows resource export from one extension that can
be imported into another extension's namespace.
BUG=236044
TEST=browser_tests ExtensionApiTest.SharedModule
TEST=unit_tests SharedModuleManifestTest.*
Review URL: https://chromiumcodereview.appspot.com/13971005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@197201 0039d316-1c4b-4281-b951-d872f2087c98
43 files changed, 809 insertions, 27 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 8bd2af8..c91c002 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -5257,6 +5257,12 @@ Make sure you do not expose any sensitive information. <message name="IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION" desc="Error displayed during installation of a side-loaded app, extension, or theme when the version of the referenced extension does not match the version the developer declared during registration."> Expected version "<ph name="EXPECTED_VERSION">$1<ex>2.0</ex></ph>", but version was "<ph name="NEW_ID">$2<ex>1.0</ex></ph>". </message> + <message name="IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_FOUND" desc="Error displayed during installation of an extension when an import dependency is not found."> + Required extension with ID "<ph name="IMPORT_ID">$1<ex>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</ex></ph>" and minimum version "<ph name="IMPORT_VERSION">$2<ex>1.0</ex></ph>" not found. + </message> + <message name="IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE" desc="Error displayed during installation of an extension which tries to imports resources from an extension which is not a shared module."> + Unable to import extension with ID "<ph name="IMPORT_ID">$1<ex>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</ex></ph>" because it is not a shared module. + </message> <message name="IDS_EXTENSION_OVERLAPPING_WEB_EXTENT" desc="Error message when a user tries to install an app with a web extent that overlaps another installed app."> Could not add application because it conflicts with "<ph name="APP_NAME">$1<ex>Google Mail</ex></ph>". </message> diff --git a/chrome/browser/extensions/admin_policy.cc b/chrome/browser/extensions/admin_policy.cc index 2cdbcd5..6500106 100644 --- a/chrome/browser/extensions/admin_policy.cc +++ b/chrome/browser/extensions/admin_policy.cc @@ -80,6 +80,7 @@ bool UserMayLoad(const base::ListValue* blacklist, case Manifest::TYPE_HOSTED_APP: case Manifest::TYPE_LEGACY_PACKAGED_APP: case Manifest::TYPE_PLATFORM_APP: + case Manifest::TYPE_SHARED_MODULE: base::FundamentalValue type_value(extension->GetType()); if (allowed_types && allowed_types->Find(type_value) == allowed_types->end()) diff --git a/chrome/browser/extensions/chrome_manifest_parser.cc b/chrome/browser/extensions/chrome_manifest_parser.cc index 233c25a..9f89bbf 100644 --- a/chrome/browser/extensions/chrome_manifest_parser.cc +++ b/chrome/browser/extensions/chrome_manifest_parser.cc @@ -13,6 +13,7 @@ #include "chrome/common/extensions/manifest_handlers/kiosk_enabled_info.h" #include "chrome/common/extensions/manifest_handlers/offline_enabled_info.h" #include "chrome/common/extensions/manifest_handlers/requirements_handler.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_service.h" @@ -29,6 +30,7 @@ ChromeManifestParser::ChromeManifestParser(Profile* profile) (new OfflineEnabledHandler)->Register(); (new OptionsPageHandler)->Register(); (new RequirementsHandler)->Register(); + (new SharedModuleHandler)->Register(); (new UpdateURLHandler)->Register(); (new URLOverridesHandler)->Register(); diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index f0a9365..2efea97 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -42,6 +42,7 @@ #include "chrome/common/extensions/feature_switch.h" #include "chrome/common/extensions/manifest.h" #include "chrome/common/extensions/manifest_handlers/icons_handler.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/resource_dispatcher_host.h" @@ -60,6 +61,7 @@ using content::BrowserThread; using content::UserMetricsAction; +using extensions::SharedModuleInfo; namespace extensions { @@ -400,14 +402,42 @@ void CrxInstaller::OnUnpackSuccess(const base::FilePath& temp_dir, if (!BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&CrxInstaller::CheckRequirements, this))) + base::Bind(&CrxInstaller::CheckImportsAndRequirements, this))) NOTREACHED(); } -void CrxInstaller::CheckRequirements() { +void CrxInstaller::CheckImportsAndRequirements() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!frontend_weak_.get() || frontend_weak_->browser_terminating()) return; + + if (SharedModuleInfo::ImportsModules(extension_)) { + const std::vector<SharedModuleInfo::ImportInfo>& imports = + SharedModuleInfo::GetImports(extension_); + std::vector<SharedModuleInfo::ImportInfo>::const_iterator i; + for (i = imports.begin(); i != imports.end(); ++i) { + Version version_required(i->minimum_version); + const Extension* imported_module = + frontend_weak_->GetExtensionById(i->extension_id, true); + if (!imported_module || + (version_required.IsValid() && + imported_module->version()->CompareTo(version_required) < 0)) { + ReportFailureFromUIThread( + CrxInstallerError(l10n_util::GetStringFUTF16( + IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_FOUND, + ASCIIToUTF16(i->extension_id), + ASCIIToUTF16(i->minimum_version)))); + return; + } + if (!SharedModuleInfo::IsSharedModule(imported_module)) { + ReportFailureFromUIThread( + CrxInstallerError(l10n_util::GetStringFUTF16( + IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE, + ASCIIToUTF16(i->extension_id)))); + return; + } + } + } AddRef(); // Balanced in OnRequirementsChecked(). requirements_checker_->Check(extension_, base::Bind(&CrxInstaller::OnRequirementsChecked, @@ -417,7 +447,7 @@ void CrxInstaller::CheckRequirements() { void CrxInstaller::OnRequirementsChecked( std::vector<std::string> requirement_errors) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - Release(); // Balanced in CheckRequirements(). + Release(); // Balanced in CheckImportsAndRequirements(). if (!requirement_errors.empty()) { if (error_on_unsupported_requirements_) { ReportFailureFromUIThread(CrxInstallerError( diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index 1913591..e394c33 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -222,7 +222,7 @@ class CrxInstaller const Extension* extension) OVERRIDE; // Called on the UI thread to start the requirements check on the extension. - void CheckRequirements(); + void CheckImportsAndRequirements(); // Runs on the UI thread. Callback from RequirementsChecker. void OnRequirementsChecked(std::vector<std::string> requirement_errors); diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc index b0da9b0..a6ed685 100644 --- a/chrome/browser/extensions/extension_protocols.cc +++ b/chrome/browser/extensions/extension_protocols.cc @@ -16,6 +16,7 @@ #include "base/stringprintf.h" #include "base/threading/thread_restrictions.h" #include "base/threading/worker_pool.h" +#include "base/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/browser/extensions/image_loader.h" @@ -26,6 +27,7 @@ #include "chrome/common/extensions/extension_file_util.h" #include "chrome/common/extensions/incognito_handler.h" #include "chrome/common/extensions/manifest_handlers/icons_handler.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "chrome/common/extensions/web_accessible_resources_handler.h" #include "chrome/common/url_constants.h" @@ -45,6 +47,7 @@ using content::ResourceRequestInfo; using extensions::Extension; +using extensions::SharedModuleInfo; namespace { @@ -205,14 +208,13 @@ class URLRequestExtensionJob : public net::URLRequestFileJob { net::NetworkDelegate* network_delegate, const std::string& extension_id, const base::FilePath& directory_path, + const base::FilePath& relative_path, const std::string& content_security_policy, bool send_cors_header) : net::URLRequestFileJob(request, network_delegate, base::FilePath()), // TODO(tc): Move all of these files into resources.pak so we don't break // when updating on Linux. - resource_(extension_id, directory_path, - extension_file_util::ExtensionURLToRelativeFilePath( - request->url())), + resource_(extension_id, directory_path, relative_path), weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { response_info_.headers = BuildHttpHeaders(content_security_policy, send_cors_header); @@ -388,7 +390,7 @@ net::URLRequestJob* ExtensionProtocolHandler::MaybeCreateJob( net::URLRequest* request, net::NetworkDelegate* network_delegate) const { // chrome-extension://extension-id/resource/path.js - const std::string& extension_id = request->url().host(); + std::string extension_id = request->url().host(); const Extension* extension = extension_info_map_->extensions().GetByID(extension_id); @@ -462,10 +464,54 @@ ExtensionProtocolHandler::MaybeCreateJob( } } + relative_path = + extension_file_util::ExtensionURLToRelativeFilePath(request->url()); + + if (SharedModuleInfo::IsImportedPath(path)) { + std::string new_extension_id; + std::string new_relative_path; + SharedModuleInfo::ParseImportedPath(path, &new_extension_id, + &new_relative_path); + const Extension* new_extension = + extension_info_map_->extensions().GetByID(new_extension_id); + + bool first_party_in_import = false; + // NB: This first_party_for_cookies call is not for security, it is only + // used so an exported extension can limit the visible surface to the + // extension that imports it, more or less constituting its API. + const std::string& first_party_path = + request->first_party_for_cookies().path(); + if (SharedModuleInfo::IsImportedPath(first_party_path)) { + std::string first_party_id; + std::string dummy; + SharedModuleInfo::ParseImportedPath(first_party_path, &first_party_id, + &dummy); + if (first_party_id == new_extension_id) { + first_party_in_import = true; + } + } + + if (SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) && + new_extension && + (first_party_in_import || + SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path))) { + directory_path = new_extension->path(); + extension_id = new_extension_id; +#if defined(OS_POSIX) + relative_path = base::FilePath(new_relative_path); +#elif defined(OS_WIN) + relative_path = base::FilePath(UTF8ToWide(new_relative_path)); +#endif + } else { + return NULL; + } + } + return new URLRequestExtensionJob(request, network_delegate, extension_id, directory_path, + relative_path, content_security_policy, send_cors_header); } diff --git a/chrome/browser/extensions/shared_module_apitest.cc b/chrome/browser/extensions/shared_module_apitest.cc new file mode 100644 index 0000000..e10a467 --- /dev/null +++ b/chrome/browser/extensions/shared_module_apitest.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2013 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_apitest.h" + +using extensions::Extension; + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, SharedModule) { + // import_pass depends on this shared module. + // NB: We use LoadExtension instead of InstallExtension here so the public-key + // in 'shared' is used to generate the extension ID so it can be imported + // correctly. We use InstallExtension otherwise so the loads happen through + // the CRX installer which validates imports. + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("shared_module").AppendASCII("shared"))); + + EXPECT_TRUE(RunExtensionTest("shared_module/import_pass")); + + EXPECT_FALSE(InstallExtension( + test_data_dir_.AppendASCII("shared_module") + .AppendASCII("import_wrong_version"), 0)); + EXPECT_FALSE(InstallExtension( + test_data_dir_.AppendASCII("shared_module") + .AppendASCII("import_non_existent"), 0)); +} diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc index 199d8d4..bcac447 100644 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc +++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc @@ -127,6 +127,7 @@ void ChromeRenderViewHostObserver::InitRenderViewForExtensions() { case Manifest::TYPE_UNKNOWN: case Manifest::TYPE_THEME: + case Manifest::TYPE_SHARED_MODULE: break; } } diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 0360a90..96a2dc4 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -265,6 +265,8 @@ 'common/extensions/manifest_handlers/requirements_handler.h', 'common/extensions/manifest_handlers/sandboxed_page_info.cc', 'common/extensions/manifest_handlers/sandboxed_page_info.h', + 'common/extensions/manifest_handlers/shared_module_info.cc', + 'common/extensions/manifest_handlers/shared_module_info.h', 'common/extensions/manifest_handlers/theme_handler.cc', 'common/extensions/manifest_handlers/theme_handler.h', 'common/extensions/manifest_url_handler.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 2ee6816..504d03a 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1392,6 +1392,7 @@ 'browser/extensions/process_management_browsertest.cc', 'browser/extensions/requirements_checker_browsertest.cc', 'browser/extensions/sandboxed_pages_apitest.cc', + 'browser/extensions/shared_module_apitest.cc', 'browser/extensions/startup_helper_browsertest.cc', 'browser/extensions/stubs_apitest.cc', 'browser/extensions/subscribe_page_action_browsertest.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 6bb1a79..2e8f54c 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1565,6 +1565,7 @@ 'common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc', 'common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc', 'common/extensions/manifest_handlers/externally_connectable_unittest.cc', + 'common/extensions/manifest_handlers/shared_module_manifest_unittest.cc', 'common/extensions/manifest_tests/extension_manifest_test.cc', 'common/extensions/manifest_tests/extension_manifests_background_unittest.cc', 'common/extensions/manifest_tests/extension_manifests_chromepermission_unittest.cc', diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json index 48e580d..11fb708 100644 --- a/chrome/common/extensions/api/_manifest_features.json +++ b/chrome/common/extensions/api/_manifest_features.json @@ -29,16 +29,16 @@ "extension_types": ["platform_app"], "min_manifest_version": 2 }, - "app.launch": { - "channel": "stable", - "extension_types": ["packaged_app", "hosted_app"] - }, "app.isolation": { "channel": "stable", // Platform apps always have isolated storage, thus they cannot specify it // via the manifest. "extension_types": ["packaged_app", "hosted_app"] }, + "app.launch": { + "channel": "stable", + "extension_types": ["packaged_app", "hosted_app"] + }, "author": { "channel": "stable", "extension_types": "all" @@ -143,6 +143,14 @@ ] } ], + "export": { + "channel": "dev", + "extension_types": ["shared_module"], + "whitelist": [ + "gpcckkmippodnppallflahfabmeilgjg", // browser and unit tests + "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F" + ] + }, "externally_connectable": { "channel": "stable", "extension_types": [ @@ -153,10 +161,6 @@ "channel": "stable", "extension_types": ["extension", "packaged_app"] }, - "media_galleries_handlers": { - "channel": "stable", - "extension_types": ["packaged_app", "platform_app"] - }, "file_handlers": { "channel": "stable", "extension_types": ["platform_app"] @@ -169,6 +173,10 @@ "channel": "stable", "extension_types": "all" }, + "import": { + "channel": "dev", + "extension_types": "all" + }, "incognito": { "channel": "stable", "extension_types": ["extension", "packaged_app"] @@ -187,10 +195,20 @@ "channel": "stable", "extension_types": "all" }, + "kiosk_enabled": { + "channel": "stable", + "extension_types": [ + "platform_app" + ] + }, "manifest_version": { "channel": "stable", "extension_types": "all" }, + "media_galleries_handlers": { + "channel": "stable", + "extension_types": ["packaged_app", "platform_app"] + }, "mime_types": { "channel": "stable", "extension_types": [ "extension", "packaged_app", "platform_app" ], @@ -218,10 +236,10 @@ "channel": "stable", "extension_types": "all" }, - "kiosk_enabled": { + "oauth2": { "channel": "stable", "extension_types": [ - "platform_app" + "extension", "packaged_app", "platform_app" ] }, "offline_enabled": { @@ -246,12 +264,6 @@ "extension", "packaged_app", "hosted_app" ] }, - "oauth2": { - "channel": "stable", - "extension_types": [ - "extension", "packaged_app", "platform_app" - ] - }, "page_action": { "channel": "stable", "extension_types": ["extension"] diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index dd0d07e..c6e8847 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -102,6 +102,9 @@ const char kDecodedMessageCatalogsFilename[] = "DECODED_MESSAGE_CATALOGS"; const char kGeneratedBackgroundPageFilename[] = "_generated_background_page.html"; + +const char kModulesDir[] = "_modules"; + } // These must match the values expected by the chrome.management extension API. diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index 27b4287..8ce965f 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -64,6 +64,9 @@ namespace extension_filenames { // The filename to use for a background page generated from // background.scripts. extern const char kGeneratedBackgroundPageFilename[]; + + // Path to imported modules. + extern const char kModulesDir[]; } // Keys in the dictionary returned by Extension::GetBasicInfo(). diff --git a/chrome/common/extensions/extension_manifest_constants.cc b/chrome/common/extensions/extension_manifest_constants.cc index 72b9d97..7dc68d5 100644 --- a/chrome/common/extensions/extension_manifest_constants.cc +++ b/chrome/common/extensions/extension_manifest_constants.cc @@ -34,6 +34,7 @@ const char kDisplayInNewTabPage[] = "display_in_new_tab_page"; const char kEventName[] = "event_name"; const char kExcludeGlobs[] = "exclude_globs"; const char kExcludeMatches[] = "exclude_matches"; +const char kExport[] = "export"; const char kExternallyConnectable[] = "externally_connectable"; const char kFileAccessList[] = "file_access"; const char kFileFilters[] = "file_filters"; @@ -45,6 +46,7 @@ const char kFileHandlerTypes[] = "types"; const char kHomepageURL[] = "homepage_url"; const char kIcons[] = "icons"; const char kId[] = "id"; +const char kImport[] = "import"; const char kIncognito[] = "incognito"; const char kIncludeGlobs[] = "include_globs"; const char kInputComponents[] = "input_components"; @@ -74,6 +76,7 @@ const char kLayouts[] = "layouts"; const char kManifestVersion[] = "manifest_version"; const char kMatches[] = "matches"; const char kMinimumChromeVersion[] = "minimum_chrome_version"; +const char kMinimumVersion[] = "minimum_version"; const char kMIMETypes[] = "mime_types"; const char kMimeTypesHandler[] = "mime_types_handler"; const char kName[] = "name"; @@ -107,6 +110,7 @@ const char kPlugins[] = "plugins"; const char kPluginsPath[] = "path"; const char kPluginsPublic[] = "public"; const char kPublicKey[] = "key"; +const char kResources[] = "resources"; const char kRequirements[] = "requirements"; const char kRunAt[] = "run_at"; const char kSandboxedPages[] = "sandbox.pages"; @@ -263,6 +267,14 @@ const char kInvalidExcludeMatch[] = "Invalid value for 'content_scripts[*].exclude_matches[*]': *"; const char kInvalidExcludeMatches[] = "Invalid value for 'content_scripts[*].exclude_matches'."; +const char kInvalidExport[] = + "Invalid value for 'export'."; +const char kInvalidExportPermissions[] = + "Permissions are not allowed for extensions that export resources."; +const char kInvalidExportResources[] = + "Invalid value for 'export.resources'."; +const char kInvalidExportResourcesString[] = + "Invalid value for 'export.resources[*]'."; const char kInvalidFileAccessList[] = "Invalid value for 'file_access'."; const char kInvalidFileAccessValue[] = @@ -293,6 +305,14 @@ const char kInvalidIconPath[] = "Invalid value for 'icons[\"*\"]'."; const char kInvalidIcons[] = "Invalid value for 'icons'."; +const char kInvalidImport[] = + "Invalid value for 'import'."; +const char kInvalidImportAndExport[] = + "Simultaneous 'import' and 'export' are not allowed."; +const char kInvalidImportId[] = + "Invalid value for 'import[*].id'."; +const char kInvalidImportVersion[] = + "Invalid value for 'import[*].minimum_version'."; const char kInvalidIncognitoBehavior[] = "Invalid value for 'incognito'."; const char kInvalidIncognitoModeForPlatformApp[] = diff --git a/chrome/common/extensions/extension_manifest_constants.h b/chrome/common/extensions/extension_manifest_constants.h index ef3c591..6270e9b 100644 --- a/chrome/common/extensions/extension_manifest_constants.h +++ b/chrome/common/extensions/extension_manifest_constants.h @@ -40,6 +40,7 @@ namespace extension_manifest_keys { extern const char kEventName[]; extern const char kExcludeGlobs[]; extern const char kExcludeMatches[]; + extern const char kExport[]; extern const char kExternallyConnectable[]; extern const char kFileAccessList[]; extern const char kFileHandlers[]; @@ -51,6 +52,7 @@ namespace extension_manifest_keys { extern const char kHomepageURL[]; extern const char kIcons[]; extern const char kId[]; + extern const char kImport[]; extern const char kIncognito[]; extern const char kIncludeGlobs[]; extern const char kInputComponents[]; @@ -82,6 +84,7 @@ namespace extension_manifest_keys { extern const char kMIMETypes[]; extern const char kMimeTypesHandler[]; extern const char kMinimumChromeVersion[]; + extern const char kMinimumVersion[]; extern const char kNaClModules[]; extern const char kNaClModulesMIMEType[]; extern const char kNaClModulesPath[]; @@ -113,6 +116,7 @@ namespace extension_manifest_keys { extern const char kPluginsPath[]; extern const char kPluginsPublic[]; extern const char kPublicKey[]; + extern const char kResources[]; extern const char kRequirements[]; extern const char kRunAt[]; extern const char kSandboxedPages[]; @@ -225,6 +229,10 @@ namespace extension_manifest_errors { extern const char kInvalidDisplayInNewTabPage[]; extern const char kInvalidExcludeMatch[]; extern const char kInvalidExcludeMatches[]; + extern const char kInvalidExport[]; + extern const char kInvalidExportPermissions[]; + extern const char kInvalidExportResources[]; + extern const char kInvalidExportResourcesString[]; extern const char kInvalidFileAccessList[]; extern const char kInvalidFileAccessValue[]; extern const char kInvalidFileBrowserHandler[]; @@ -241,6 +249,10 @@ namespace extension_manifest_errors { extern const char kInvalidHomepageURL[]; extern const char kInvalidIconPath[]; extern const char kInvalidIcons[]; + extern const char kInvalidImport[]; + extern const char kInvalidImportAndExport[]; + extern const char kInvalidImportId[]; + extern const char kInvalidImportVersion[]; extern const char kInvalidIncognitoBehavior[]; extern const char kInvalidIncognitoModeForPlatformApp[]; extern const char kInvalidInputComponents[]; diff --git a/chrome/common/extensions/features/base_feature_provider_unittest.cc b/chrome/common/extensions/features/base_feature_provider_unittest.cc index 1670f9f..a1de8b5 100644 --- a/chrome/common/extensions/features/base_feature_provider_unittest.cc +++ b/chrome/common/extensions/features/base_feature_provider_unittest.cc @@ -21,7 +21,7 @@ TEST_F(BaseFeatureProviderTest, ManifestFeatures) { SimpleFeature* feature = static_cast<SimpleFeature*>(provider->GetFeature("description")); ASSERT_TRUE(feature); - EXPECT_EQ(5u, feature->extension_types()->size()); + EXPECT_EQ(6u, feature->extension_types()->size()); EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_EXTENSION)); EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_LEGACY_PACKAGED_APP)); @@ -29,6 +29,8 @@ TEST_F(BaseFeatureProviderTest, ManifestFeatures) { feature->extension_types()->count(Manifest::TYPE_PLATFORM_APP)); EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_HOSTED_APP)); EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_THEME)); + EXPECT_EQ(1u, + feature->extension_types()->count(Manifest::TYPE_SHARED_MODULE)); base::DictionaryValue manifest; manifest.SetString("name", "test extension"); diff --git a/chrome/common/extensions/features/simple_feature.cc b/chrome/common/extensions/features/simple_feature.cc index 20d91f15..23481f0 100644 --- a/chrome/common/extensions/features/simple_feature.cc +++ b/chrome/common/extensions/features/simple_feature.cc @@ -28,6 +28,7 @@ struct Mappings { extension_types["packaged_app"] = Manifest::TYPE_LEGACY_PACKAGED_APP; extension_types["hosted_app"] = Manifest::TYPE_HOSTED_APP; extension_types["platform_app"] = Manifest::TYPE_PLATFORM_APP; + extension_types["shared_module"] = Manifest::TYPE_SHARED_MODULE; contexts["blessed_extension"] = Feature::BLESSED_EXTENSION_CONTEXT; contexts["unblessed_extension"] = Feature::UNBLESSED_EXTENSION_CONTEXT; @@ -165,6 +166,8 @@ std::string GetDisplayTypeName(Manifest::Type type) { return "theme"; case Manifest::TYPE_USER_SCRIPT: return "user script"; + case Manifest::TYPE_SHARED_MODULE: + return "shared module"; } NOTREACHED(); diff --git a/chrome/common/extensions/features/simple_feature_unittest.cc b/chrome/common/extensions/features/simple_feature_unittest.cc index 4f92a34..2fd6d21 100644 --- a/chrome/common/extensions/features/simple_feature_unittest.cc +++ b/chrome/common/extensions/features/simple_feature_unittest.cc @@ -377,16 +377,18 @@ TEST_F(ExtensionSimpleFeatureTest, ParsePackageTypes) { extension_types->Append(new base::StringValue("packaged_app")); extension_types->Append(new base::StringValue("hosted_app")); extension_types->Append(new base::StringValue("platform_app")); + extension_types->Append(new base::StringValue("shared_module")); value->Set("extension_types", extension_types); scoped_ptr<SimpleFeature> feature(new SimpleFeature()); feature->Parse(value.get()); - EXPECT_EQ(5u, feature->extension_types()->size()); + EXPECT_EQ(6u, feature->extension_types()->size()); EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_EXTENSION)); EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_THEME)); EXPECT_TRUE(feature->extension_types()->count( Manifest::TYPE_LEGACY_PACKAGED_APP)); EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_HOSTED_APP)); EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_PLATFORM_APP)); + EXPECT_TRUE(feature->extension_types()->count(Manifest::TYPE_SHARED_MODULE)); value->SetString("extension_types", "all"); scoped_ptr<SimpleFeature> feature2(new SimpleFeature()); diff --git a/chrome/common/extensions/manifest.cc b/chrome/common/extensions/manifest.cc index 78ae189..b57e16d 100644 --- a/chrome/common/extensions/manifest.cc +++ b/chrome/common/extensions/manifest.cc @@ -106,6 +106,8 @@ Manifest::Manifest(Location location, scoped_ptr<base::DictionaryValue> value) type_(TYPE_UNKNOWN) { if (value_->HasKey(keys::kTheme)) { type_ = TYPE_THEME; + } else if (value_->HasKey(keys::kExport)) { + type_ = TYPE_SHARED_MODULE; } else if (value_->HasKey(keys::kApp)) { if (value_->Get(keys::kWebURLs, NULL) || value_->Get(keys::kLaunchWebURL, NULL)) { diff --git a/chrome/common/extensions/manifest.h b/chrome/common/extensions/manifest.h index c188793..7c37628 100644 --- a/chrome/common/extensions/manifest.h +++ b/chrome/common/extensions/manifest.h @@ -55,7 +55,8 @@ class Manifest { // This is marked legacy because platform apps are preferred. For // backwards compatibility, we can't remove support for packaged apps TYPE_LEGACY_PACKAGED_APP, - TYPE_PLATFORM_APP + TYPE_PLATFORM_APP, + TYPE_SHARED_MODULE }; // Given two install sources, return the one which should take priority @@ -122,6 +123,7 @@ class Manifest { return type_ == TYPE_LEGACY_PACKAGED_APP; } bool is_extension() const { return type_ == TYPE_EXTENSION; } + bool is_shared_module() const { return type_ == TYPE_SHARED_MODULE; } // These access the wrapped manifest value, returning false when the property // does not exist or if the manifest type can't access it. diff --git a/chrome/common/extensions/manifest_handlers/shared_module_info.cc b/chrome/common/extensions/manifest_handlers/shared_module_info.cc new file mode 100644 index 0000000..3ead016 --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/shared_module_info.cc @@ -0,0 +1,229 @@ +// Copyright (c) 2013 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/common/extensions/manifest_handlers/shared_module_info.h" + +#include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "base/version.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/extension_manifest_constants.h" +#include "chrome/common/extensions/permissions/permission_set.h" +#include "extensions/common/error_utils.h" + +using base::DictionaryValue; +namespace keys = extension_manifest_keys; +namespace values = extension_manifest_values; +namespace errors = extension_manifest_errors; + +namespace extensions { + +namespace { + +const char kSharedModule[] = "shared_module"; + +static base::LazyInstance<SharedModuleInfo> g_empty_shared_module_info = + LAZY_INSTANCE_INITIALIZER; + +const SharedModuleInfo& GetSharedModuleInfo(const Extension* extension) { + SharedModuleInfo* info = static_cast<SharedModuleInfo*>( + extension->GetManifestData(kSharedModule)); + if (!info) + return g_empty_shared_module_info.Get(); + return *info; +} + +} // namespace + +SharedModuleInfo::SharedModuleInfo() { +} + +SharedModuleInfo::~SharedModuleInfo() { +} + +// static +void SharedModuleInfo::ParseImportedPath(const std::string& path, + std::string* import_id, + std::string* import_relative_path) { + std::vector<std::string> tokens; + Tokenize(path, std::string("/"), &tokens); + if (tokens.size() > 2 && tokens[0] == extension_filenames::kModulesDir && + Extension::IdIsValid(tokens[1])) { + *import_id = tokens[1]; + *import_relative_path = tokens[2]; + for (size_t i = 3; i < tokens.size(); ++i) + *import_relative_path += "/" + tokens[i]; + } +} + +// static +bool SharedModuleInfo::IsImportedPath(const std::string& path) { + std::vector<std::string> tokens; + Tokenize(path, std::string("/"), &tokens); + if (tokens.size() > 2 && tokens[0] == extension_filenames::kModulesDir && + Extension::IdIsValid(tokens[1])) { + return true; + } + return false; +} + +// static +bool SharedModuleInfo::IsSharedModule(const Extension* extension) { + CHECK(extension); + return extension->manifest()->is_shared_module(); +} + +// static +bool SharedModuleInfo::IsExportAllowed(const Extension* extension, + const std::string& relative_path) { + return GetSharedModuleInfo(extension). + exported_set_.MatchesURL(extension->url().Resolve(relative_path)); +} + +// static +bool SharedModuleInfo::ImportsExtensionById(const Extension* extension, + const std::string& other_id) { + const SharedModuleInfo& info = GetSharedModuleInfo(extension); + for (size_t i = 0; i < info.imports_.size(); i++) { + if (info.imports_[i].extension_id == other_id) + return true; + } + return false; +} + +// static +bool SharedModuleInfo::ImportsModules(const Extension* extension) { + return GetSharedModuleInfo(extension).imports_.size() > 0; +} + +// static +const std::vector<SharedModuleInfo::ImportInfo>& SharedModuleInfo::GetImports( + const Extension* extension) { + return GetSharedModuleInfo(extension).imports_; +} + +bool SharedModuleInfo::Parse(const Extension* extension, string16* error) { + bool has_import = extension->manifest()->HasKey(keys::kImport); + bool has_export = extension->manifest()->HasKey(keys::kExport); + if (!has_import && !has_export) + return true; + + if (has_import && has_export) { + *error = ASCIIToUTF16(errors::kInvalidImportAndExport); + return false; + } + + if (has_export) { + const DictionaryValue* export_value = NULL; + if (!extension->manifest()->GetDictionary(keys::kExport, &export_value)) { + *error = ASCIIToUTF16(errors::kInvalidExport); + return false; + } + const ListValue* resources_list = NULL; + if (!export_value->GetList(keys::kResources, &resources_list)) { + *error = ASCIIToUTF16(errors::kInvalidExportResources); + return false; + } + for (size_t i = 0; i < resources_list->GetSize(); ++i) { + std::string resource_path; + if (!resources_list->GetString(i, &resource_path)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidExportResourcesString, base::IntToString(i)); + return false; + } + const GURL& resolved_path = extension->url().Resolve(resource_path); + if (!resolved_path.is_valid()) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidExportResourcesString, base::IntToString(i)); + return false; + } + exported_set_.AddPattern( + URLPattern(URLPattern::SCHEME_EXTENSION, resolved_path.spec())); + } + } + + if (has_import) { + const ListValue* import_list = NULL; + if (!extension->manifest()->GetList(keys::kImport, &import_list)) { + *error = ASCIIToUTF16(errors::kInvalidImport); + return false; + } + for (size_t i = 0; i < import_list->GetSize(); ++i) { + const DictionaryValue* import_entry = NULL; + if (!import_list->GetDictionary(i, &import_entry)) { + *error = ASCIIToUTF16(errors::kInvalidImport); + return false; + } + std::string extension_id; + imports_.push_back(ImportInfo()); + if (!import_entry->GetString(keys::kId, &extension_id) || + !Extension::IdIsValid(extension_id)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidImportId, base::IntToString(i)); + return false; + } + imports_.back().extension_id = extension_id; + if (import_entry->HasKey(keys::kMinimumVersion)) { + std::string min_version; + if (!import_entry->GetString(keys::kMinimumVersion, &min_version)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidImportVersion, base::IntToString(i)); + return false; + } + imports_.back().minimum_version = min_version; + Version v(min_version); + if (!v.IsValid()) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidImportVersion, base::IntToString(i)); + return false; + } + } + } + } + return true; +} + + +SharedModuleHandler::SharedModuleHandler() { +} + +SharedModuleHandler::~SharedModuleHandler() { +} + +bool SharedModuleHandler::Parse(Extension* extension, string16* error) { + scoped_ptr<SharedModuleInfo> info(new SharedModuleInfo); + if (!info->Parse(extension, error)) + return false; + extension->SetManifestData(kSharedModule, info.release()); + return true; +} + +bool SharedModuleHandler::Validate( + const Extension* extension, + std::string* error, + std::vector<InstallWarning>* warnings) const { + // Extensions that export resources should not have any permissions of their + // own, instead they rely on the permissions of the extensions which import + // them. + if (SharedModuleInfo::IsSharedModule(extension) && + !extension->GetActivePermissions()->IsEmpty()) { + *error = errors::kInvalidExportPermissions; + return false; + } + return true; +} + +const std::vector<std::string> SharedModuleHandler::Keys() const { + static const char* keys[] = { + keys::kExport, + keys::kImport + }; + return std::vector<std::string>(keys, keys + arraysize(keys)); +} + +} // extensions + diff --git a/chrome/common/extensions/manifest_handlers/shared_module_info.h b/chrome/common/extensions/manifest_handlers/shared_module_info.h new file mode 100644 index 0000000..f76725a --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/shared_module_info.h @@ -0,0 +1,71 @@ +// Copyright (c) 2013 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. + +#ifndef CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_SHARED_MODULE_INFO_H_ +#define CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_SHARED_MODULE_INFO_H_ + +#include <string> +#include <vector> + +#include "base/values.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handler.h" + +namespace extensions { + +class SharedModuleInfo : public Extension::ManifestData { + public: + SharedModuleInfo(); + virtual ~SharedModuleInfo(); + + bool Parse(const Extension* extension, string16* error); + + struct ImportInfo { + std::string extension_id; + std::string minimum_version; + }; + + // Utility functions. + static void ParseImportedPath(const std::string& path, + std::string* import_id, + std::string* import_relative_path); + static bool IsImportedPath(const std::string& path); + + // Functions relating to exporting resources. + static bool IsSharedModule(const Extension* extension); + static bool IsExportAllowed(const Extension* extension, + const std::string& relative_path); + + // Functions relating to importing resources. + static bool ImportsExtensionById(const Extension* extension, + const std::string& other_id); + static bool ImportsModules(const Extension* extension); + static const std::vector<ImportInfo>& GetImports(const Extension* extension); + + private: + // This extension exports the following resources to other extensions. + URLPatternSet exported_set_; + + // Optional list of module imports of other extensions. + std::vector<ImportInfo> imports_; +}; + +// Parses all import/export keys in the manifest. +class SharedModuleHandler : public ManifestHandler { + public: + SharedModuleHandler(); + virtual ~SharedModuleHandler(); + + virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual bool Validate(const Extension* extension, + std::string* error, + std::vector<InstallWarning>* warnings) const OVERRIDE; + + private: + virtual const std::vector<std::string> Keys() const OVERRIDE; +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_SHARED_MODULE_INFO_H_ diff --git a/chrome/common/extensions/manifest_handlers/shared_module_manifest_unittest.cc b/chrome/common/extensions/manifest_handlers/shared_module_manifest_unittest.cc new file mode 100644 index 0000000..b4eb0bd --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/shared_module_manifest_unittest.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2013 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 "base/version.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_unittest.h" +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" +#include "chrome/common/extensions/manifest_tests/extension_manifest_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char* kValidImportPath = + "_modules/abcdefghijklmnopabcdefghijklmnop/foo/bar.html"; +const char* kValidImportPathID = "abcdefghijklmnopabcdefghijklmnop"; +const char* kValidImportPathRelative = "foo/bar.html"; +const char* kInvalidImportPath = "_modules/abc/foo.html"; +const char* kImportId1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +const char* kImportId2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; +const char* kNoImport = "cccccccccccccccccccccccccccccccc"; + +} + +namespace extensions { + +class SharedModuleManifestTest : public ExtensionManifestTest { + protected: + virtual void SetUp() OVERRIDE { + ExtensionManifestTest::SetUp(); + (new SharedModuleHandler)->Register(); + } +}; + +TEST_F(SharedModuleManifestTest, ExportsAll) { + Manifest manifest("shared_module_export.json"); + + scoped_refptr<Extension> extension = LoadAndExpectSuccess(manifest); + + EXPECT_TRUE(SharedModuleInfo::IsSharedModule(extension)) << manifest.name(); + EXPECT_FALSE(SharedModuleInfo::ImportsModules(extension)) << manifest.name(); + EXPECT_TRUE(SharedModuleInfo::IsExportAllowed(extension, "foo")) + << manifest.name(); + EXPECT_TRUE(SharedModuleInfo::IsExportAllowed(extension, "foo/bar")) + << manifest.name(); +} + +TEST_F(SharedModuleManifestTest, ExportFoo) { + Manifest manifest("shared_module_export_foo.json"); + + scoped_refptr<Extension> extension = LoadAndExpectSuccess(manifest); + + EXPECT_TRUE(SharedModuleInfo::IsSharedModule(extension)) << manifest.name(); + EXPECT_FALSE(SharedModuleInfo::ImportsModules(extension)) << manifest.name(); + EXPECT_TRUE(SharedModuleInfo::IsExportAllowed(extension, "foo")) + << manifest.name(); + EXPECT_FALSE(SharedModuleInfo::IsExportAllowed(extension, "foo/bar")) + << manifest.name(); +} + +TEST_F(SharedModuleManifestTest, ExportParseErrors) { + Testcase testcases[] = { + Testcase("shared_module_export_and_import.json", + "Simultaneous 'import' and 'export' are not allowed."), + Testcase("shared_module_export_not_dict.json", + "Invalid value for 'export'."), + Testcase("shared_module_export_resources_not_list.json", + "Invalid value for 'export.resources'."), + Testcase("shared_module_export_resource_not_string.json", + "Invalid value for 'export.resources[1]'."), + }; + RunTestcases(testcases, arraysize(testcases), EXPECT_TYPE_ERROR); +} + +TEST_F(SharedModuleManifestTest, SharedModuleStaticFunctions) { + EXPECT_TRUE(SharedModuleInfo::IsImportedPath(kValidImportPath)); + EXPECT_FALSE(SharedModuleInfo::IsImportedPath(kInvalidImportPath)); + + std::string id; + std::string relative; + SharedModuleInfo::ParseImportedPath(kValidImportPath, &id, &relative); + EXPECT_EQ(id, kValidImportPathID); + EXPECT_EQ(relative, kValidImportPathRelative); +} + +TEST_F(SharedModuleManifestTest, Import) { + Manifest manifest("shared_module_import.json"); + + scoped_refptr<Extension> extension = LoadAndExpectSuccess(manifest); + + EXPECT_FALSE(SharedModuleInfo::IsSharedModule(extension)) << manifest.name(); + EXPECT_TRUE(SharedModuleInfo::ImportsModules(extension)) << manifest.name(); + const std::vector<SharedModuleInfo::ImportInfo>& imports = + SharedModuleInfo::GetImports(extension); + ASSERT_EQ(2U, imports.size()); + EXPECT_EQ(imports[0].extension_id, kImportId1); + EXPECT_EQ(imports[0].minimum_version, ""); + EXPECT_EQ(imports[1].extension_id, kImportId2); + EXPECT_TRUE(base::Version(imports[1].minimum_version).IsValid()); + EXPECT_TRUE(SharedModuleInfo::ImportsExtensionById(extension, kImportId1)); + EXPECT_TRUE(SharedModuleInfo::ImportsExtensionById(extension, kImportId2)); + EXPECT_FALSE(SharedModuleInfo::ImportsExtensionById(extension, kNoImport)); +} + +TEST_F(SharedModuleManifestTest, ImportParseErrors) { + Testcase testcases[] = { + Testcase("shared_module_import_not_list.json", + "Invalid value for 'import'."), + Testcase("shared_module_import_invalid_id.json", + "Invalid value for 'import[0].id'."), + Testcase("shared_module_import_invalid_version.json", + "Invalid value for 'import[0].minimum_version'."), + }; + RunTestcases(testcases, arraysize(testcases), EXPECT_TYPE_ERROR); +} + +} // namespace extensions diff --git a/chrome/common/extensions/manifest_unittest.cc b/chrome/common/extensions/manifest_unittest.cc index 6805022..8845ae0 100644 --- a/chrome/common/extensions/manifest_unittest.cc +++ b/chrome/common/extensions/manifest_unittest.cc @@ -36,6 +36,8 @@ class ManifestTest : public testing::Test { EXPECT_EQ(type == Manifest::TYPE_LEGACY_PACKAGED_APP, manifest->is_legacy_packaged_app()); EXPECT_EQ(type == Manifest::TYPE_HOSTED_APP, manifest->is_hosted_app()); + EXPECT_EQ(type == Manifest::TYPE_SHARED_MODULE, + manifest->is_shared_module()); } // Helper function that replaces the Manifest held by |manifest| with a copy @@ -138,6 +140,13 @@ TEST_F(ManifestTest, ExtensionTypes) { MutateManifest( &manifest, keys::kTheme, NULL); + // Shared module. + MutateManifest( + &manifest, keys::kExport, new base::DictionaryValue()); + AssertType(manifest.get(), Manifest::TYPE_SHARED_MODULE); + MutateManifest( + &manifest, keys::kExport, NULL); + // Packaged app. MutateManifest( &manifest, keys::kApp, new base::DictionaryValue()); diff --git a/chrome/test/data/extensions/api_test/shared_module/import_non_existent/manifest.json b/chrome/test/data/extensions/api_test/shared_module/import_non_existent/manifest.json new file mode 100644 index 0000000..87b0a70 --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/import_non_existent/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "Shared Module Import Non Existent", + "manifest_version": 2, + "version": "1.0", + "import": [{ + "id": "cccccccccccccccccccccccccccccccc" + } + ] +} diff --git a/chrome/test/data/extensions/api_test/shared_module/import_pass/check.js b/chrome/test/data/extensions/api_test/shared_module/import_pass/check.js new file mode 100644 index 0000000..37ca2a0 --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/import_pass/check.js @@ -0,0 +1,11 @@ +// Copyright (c) 2013 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. + +if (typeof fail_exported != 'undefined') + chrome.test.notifyFail('fail.js was unintentionally exported.'); + +if (typeof pass_exported == 'undefined') + chrome.test.notifyFail('pass.js was not exported correctly.'); + +chrome.test.notifyPass(); diff --git a/chrome/test/data/extensions/api_test/shared_module/import_pass/main.html b/chrome/test/data/extensions/api_test/shared_module/import_pass/main.html new file mode 100644 index 0000000..1c412ad --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/import_pass/main.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<!-- fail.js is not exported, it would be an error to have it load. --> +<script src="_modules/gpcckkmippodnppallflahfabmeilgjg/fail.js"></script> +<script src="_modules/gpcckkmippodnppallflahfabmeilgjg/pass.js"></script> +<script src="check.js"></script> diff --git a/chrome/test/data/extensions/api_test/shared_module/import_pass/manifest.json b/chrome/test/data/extensions/api_test/shared_module/import_pass/manifest.json new file mode 100644 index 0000000..a9ed377 --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/import_pass/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "Shared Module Import Test", + "manifest_version": 2, + "version": "1.0", + "app": { + "background": { + "page": "main.html" + } + }, + "import": [{ + "id": "gpcckkmippodnppallflahfabmeilgjg" + } + ] +} diff --git a/chrome/test/data/extensions/api_test/shared_module/import_wrong_version/manifest.json b/chrome/test/data/extensions/api_test/shared_module/import_wrong_version/manifest.json new file mode 100644 index 0000000..5d815b5 --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/import_wrong_version/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Shared Module Import Test", + "manifest_version": 2, + "version": "1.0", + "import": [{ + "id": "gpcckkmippodnppallflahfabmeilgjg", + "minimum_version": "2.0" + } + ] +} diff --git a/chrome/test/data/extensions/api_test/shared_module/shared/fail.js b/chrome/test/data/extensions/api_test/shared_module/shared/fail.js new file mode 100644 index 0000000..dbe933d --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/shared/fail.js @@ -0,0 +1,5 @@ +// Copyright (c) 2013 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. + +var fail_exported = {}; diff --git a/chrome/test/data/extensions/api_test/shared_module/shared/manifest.json b/chrome/test/data/extensions/api_test/shared_module/shared/manifest.json new file mode 100644 index 0000000..6a90fa4 --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/shared/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Shared Module Test", + "manifest_version": 2, + "version": "1.0", + "export": { + "resources": [ + "pass.js" + ] + }, + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2ADgfXh2Yh6uLgpBrls/gtQEp5gLWZedwRV1Cyy4R1giRnGeYwZ8pgmtpkjdSfoNIrx/WfiXDF+K2TF2I9V+LM60FqqoY6uJNmXjByzKFjNtFETXthA+v/zv6uX1NJ3m+GN9tdDR53AC7ws28Zi0S4+n/a++uzDY+aDubrp+PwIDAQAB" +} diff --git a/chrome/test/data/extensions/api_test/shared_module/shared/pass.js b/chrome/test/data/extensions/api_test/shared_module/shared/pass.js new file mode 100644 index 0000000..2e54dea --- /dev/null +++ b/chrome/test/data/extensions/api_test/shared_module/shared/pass.js @@ -0,0 +1,5 @@ +// Copyright (c) 2013 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. + +var pass_exported = {}; diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_export.json b/chrome/test/data/extensions/manifest_tests/shared_module_export.json new file mode 100644 index 0000000..aafb5f8 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_export.json @@ -0,0 +1,11 @@ +{ + "name": "shared_module_export unittest", + "manifest_version": 2, + "version": "1.0", + "export": { + "resources": [ + "*" + ] + }, + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2ADgfXh2Yh6uLgpBrls/gtQEp5gLWZedwRV1Cyy4R1giRnGeYwZ8pgmtpkjdSfoNIrx/WfiXDF+K2TF2I9V+LM60FqqoY6uJNmXjByzKFjNtFETXthA+v/zv6uX1NJ3m+GN9tdDR53AC7ws28Zi0S4+n/a++uzDY+aDubrp+PwIDAQAB" +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_export_and_import.json b/chrome/test/data/extensions/manifest_tests/shared_module_export_and_import.json new file mode 100644 index 0000000..3f59ac2 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_export_and_import.json @@ -0,0 +1,14 @@ +{ + "name": "shared_module_export unittest", + "manifest_version": 2, + "version": "1.0", + "export": { + "resources": [ + "*" + ] + }, + "import": [{ + "id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }], + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2ADgfXh2Yh6uLgpBrls/gtQEp5gLWZedwRV1Cyy4R1giRnGeYwZ8pgmtpkjdSfoNIrx/WfiXDF+K2TF2I9V+LM60FqqoY6uJNmXjByzKFjNtFETXthA+v/zv6uX1NJ3m+GN9tdDR53AC7ws28Zi0S4+n/a++uzDY+aDubrp+PwIDAQAB" +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_export_foo.json b/chrome/test/data/extensions/manifest_tests/shared_module_export_foo.json new file mode 100644 index 0000000..e0b583f --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_export_foo.json @@ -0,0 +1,11 @@ +{ + "name": "shared_module_export unittest", + "manifest_version": 2, + "version": "1.0", + "export": { + "resources": [ + "foo" + ] + }, + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2ADgfXh2Yh6uLgpBrls/gtQEp5gLWZedwRV1Cyy4R1giRnGeYwZ8pgmtpkjdSfoNIrx/WfiXDF+K2TF2I9V+LM60FqqoY6uJNmXjByzKFjNtFETXthA+v/zv6uX1NJ3m+GN9tdDR53AC7ws28Zi0S4+n/a++uzDY+aDubrp+PwIDAQAB" +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_export_not_dict.json b/chrome/test/data/extensions/manifest_tests/shared_module_export_not_dict.json new file mode 100644 index 0000000..2500644 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_export_not_dict.json @@ -0,0 +1,9 @@ +{ + "name": "shared_module_export unittest", + "manifest_version": 2, + "version": "1.0", + "export": [ + {"resources": "*"} + ], + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2ADgfXh2Yh6uLgpBrls/gtQEp5gLWZedwRV1Cyy4R1giRnGeYwZ8pgmtpkjdSfoNIrx/WfiXDF+K2TF2I9V+LM60FqqoY6uJNmXjByzKFjNtFETXthA+v/zv6uX1NJ3m+GN9tdDR53AC7ws28Zi0S4+n/a++uzDY+aDubrp+PwIDAQAB" +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_export_resource_not_string.json b/chrome/test/data/extensions/manifest_tests/shared_module_export_resource_not_string.json new file mode 100644 index 0000000..2021553 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_export_resource_not_string.json @@ -0,0 +1,12 @@ +{ + "name": "shared_module_export unittest", + "manifest_version": 2, + "version": "1.0", + "export": { + "resources": [ + "*", + {} + ] + }, + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2ADgfXh2Yh6uLgpBrls/gtQEp5gLWZedwRV1Cyy4R1giRnGeYwZ8pgmtpkjdSfoNIrx/WfiXDF+K2TF2I9V+LM60FqqoY6uJNmXjByzKFjNtFETXthA+v/zv6uX1NJ3m+GN9tdDR53AC7ws28Zi0S4+n/a++uzDY+aDubrp+PwIDAQAB" +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_export_resources_not_list.json b/chrome/test/data/extensions/manifest_tests/shared_module_export_resources_not_list.json new file mode 100644 index 0000000..76971072 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_export_resources_not_list.json @@ -0,0 +1,9 @@ +{ + "name": "shared_module_export unittest", + "manifest_version": 2, + "version": "1.0", + "export": { + "resources": "*" + }, + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2ADgfXh2Yh6uLgpBrls/gtQEp5gLWZedwRV1Cyy4R1giRnGeYwZ8pgmtpkjdSfoNIrx/WfiXDF+K2TF2I9V+LM60FqqoY6uJNmXjByzKFjNtFETXthA+v/zv6uX1NJ3m+GN9tdDR53AC7ws28Zi0S4+n/a++uzDY+aDubrp+PwIDAQAB" +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_import.json b/chrome/test/data/extensions/manifest_tests/shared_module_import.json new file mode 100644 index 0000000..ca9e170 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_import.json @@ -0,0 +1,14 @@ +{ + "name": "Import unit test", + "manifest_version": 2, + "version": "1.0", + "import": [ + { + "id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "minimum_version": "1.0.0.0" + } + ] +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_import_invalid_id.json b/chrome/test/data/extensions/manifest_tests/shared_module_import_invalid_id.json new file mode 100644 index 0000000..aadd9ae --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_import_invalid_id.json @@ -0,0 +1,10 @@ +{ + "name": "Import unit test", + "manifest_version": 2, + "version": "1.0", + "import": [ + { + "id": "xyz" + } + ] +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_import_invalid_version.json b/chrome/test/data/extensions/manifest_tests/shared_module_import_invalid_version.json new file mode 100644 index 0000000..77a921c --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_import_invalid_version.json @@ -0,0 +1,11 @@ +{ + "name": "Import unit test", + "manifest_version": 2, + "version": "1.0", + "import": [ + { + "id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "minimum_version": "aaa" + } + ] +} diff --git a/chrome/test/data/extensions/manifest_tests/shared_module_import_not_list.json b/chrome/test/data/extensions/manifest_tests/shared_module_import_not_list.json new file mode 100644 index 0000000..a989bd9 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/shared_module_import_not_list.json @@ -0,0 +1,8 @@ +{ + "name": "Import unit test", + "manifest_version": 2, + "version": "1.0", + "import": { + "id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } +} |