diff options
author | groby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-23 00:35:13 +0000 |
---|---|---|
committer | groby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-23 00:35:13 +0000 |
commit | be5f00786d8da88985225d545395654e15bd3d9e (patch) | |
tree | 216ea5cc6bdb86e76727c64301516609c9927a77 | |
parent | ea8455ce1976885d5d730b1349557e05c195d00d (diff) | |
download | chromium_src-be5f00786d8da88985225d545395654e15bd3d9e.zip chromium_src-be5f00786d8da88985225d545395654e15bd3d9e.tar.gz chromium_src-be5f00786d8da88985225d545395654e15bd3d9e.tar.bz2 |
Extension loading extracts intents from Manifest data.
BUG=none
TEST=ExtensionManifestTest.WebIntents
Review URL: http://codereview.chromium.org/7917006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102409 0039d316-1c4b-4281-b951-d872f2087c98
14 files changed, 227 insertions, 1 deletions
diff --git a/chrome/browser/extensions/extension_icon_manager_unittest.cc b/chrome/browser/extensions/extension_icon_manager_unittest.cc index a607704..8f81fd5 100644 --- a/chrome/browser/extensions/extension_icon_manager_unittest.cc +++ b/chrome/browser/extensions/extension_icon_manager_unittest.cc @@ -109,9 +109,10 @@ TEST_F(ExtensionIconManagerTest, LoadRemoveLoad) { static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL))); ASSERT_TRUE(manifest.get() != NULL); + std::string error; scoped_refptr<Extension> extension(Extension::Create( manifest_path.DirName(), Extension::INVALID, *manifest.get(), - Extension::STRICT_ERROR_CHECKS, NULL)); + Extension::STRICT_ERROR_CHECKS, &error)); ASSERT_TRUE(extension.get()); TestIconManager icon_manager(this); diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index af1f538..e595749 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -44,6 +44,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "webkit/glue/image_decoder.h" +#include "webkit/glue/web_intent_service_data.h" namespace keys = extension_manifest_keys; namespace values = extension_manifest_values; @@ -1162,6 +1163,74 @@ bool Extension::LoadAppIsolation(const DictionaryValue* manifest, return true; } +bool Extension::LoadWebIntents(const base::DictionaryValue& manifest, + std::string* error) { + DCHECK(error); + + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableWebIntents)) + return true; + + if (!manifest.HasKey(keys::kIntents)) + return true; + + DictionaryValue* all_intents = NULL; + if (!manifest.GetDictionary(keys::kIntents, &all_intents)) { + *error = errors::kInvalidIntents; + return false; + } + + std::string value; + for (DictionaryValue::key_iterator iter(all_intents->begin_keys()); + iter != all_intents->end_keys(); ++iter) { + WebIntentServiceData intent; + + DictionaryValue* one_intent = NULL; + if (!all_intents->GetDictionaryWithoutPathExpansion(*iter, &one_intent)) { + *error = errors::kInvalidIntent; + return false; + } + intent.action = UTF8ToUTF16(*iter); + + // TODO(groby): Support an array of types. + if (one_intent->HasKey(keys::kIntentType) && + !one_intent->GetString(keys::kIntentType, &intent.type)) { + *error = errors::kInvalidIntentType; + return false; + } + + if (one_intent->HasKey(keys::kIntentPath)) { + if (!one_intent->GetString(keys::kIntentPath, &value)) { + *error = errors::kInvalidIntentPath; + return false; + } + intent.service_url = GetResourceURL(value); + } + + if (one_intent->HasKey(keys::kIntentTitle) && + !one_intent->GetString(keys::kIntentTitle, &intent.title)) { + *error = errors::kInvalidIntentTitle; + return false; + } + + if (one_intent->HasKey(keys::kIntentDisposition)) { + if (!one_intent->GetString(keys::kIntentDisposition, &value) || + (value != values::kIntentDispositionWindow && + value != values::kIntentDispositionInline)) { + *error = errors::kInvalidIntentDisposition; + return false; + } + if (value == values::kIntentDispositionInline) + intent.disposition = WebIntentServiceData::DISPOSITION_INLINE; + else + intent.disposition = WebIntentServiceData::DISPOSITION_WINDOW; + } + + intents_.push_back(intent); + } + return true; +} + + bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, std::string* error) { if (web_extent().is_empty()) @@ -2272,6 +2341,10 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, } } + // Initialize web intents (optional). + if (!LoadWebIntents(source, error)) + return false; + // Initialize incognito behavior. Apps default to split mode, extensions // default to spanning. incognito_split_mode_ = is_app(); diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 215912e..19c9cbe 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -25,6 +25,7 @@ #include "chrome/common/extensions/url_pattern_set.h" #include "googleurl/src/gurl.h" #include "ui/gfx/size.h" +#include "webkit/glue/web_intent_service_data.h" class ExtensionAction; class ExtensionResource; @@ -533,6 +534,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool incognito_split_mode() const { return incognito_split_mode_; } bool offline_enabled() const { return offline_enabled_; } const std::vector<TtsVoice>& tts_voices() const { return tts_voices_; } + const std::vector<WebIntentServiceData>& intents() const { return intents_; } bool wants_file_access() const { return wants_file_access_; } int creation_flags() const { return creation_flags_; } @@ -641,6 +643,8 @@ class Extension : public base::RefCountedThreadSafe<Extension> { std::string* error); bool LoadAppIsolation(const base::DictionaryValue* manifest, std::string* error); + bool LoadWebIntents(const base::DictionaryValue& manifest, + std::string* error); bool EnsureNotHybridApp(const base::DictionaryValue* manifest, std::string* error); @@ -843,6 +847,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // List of text-to-speech voices that this extension provides, if any. std::vector<TtsVoice> tts_voices_; + // List of intents that this extension provides, if any. + std::vector<WebIntentServiceData> intents_; + // Whether the extension has host permissions or user script patterns that // imply access to file:/// scheme URLs (the user may not have actually // granted it that access). diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 242dae5..91000a5 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -38,6 +38,11 @@ const char* kId = "id"; const char* kIncognito = "incognito"; const char* kIncludeGlobs = "include_globs"; const char* kInputComponents = "input_components"; +const char* kIntents = "intents"; +const char* kIntentType = "type"; +const char* kIntentPath = "path"; +const char* kIntentTitle = "title"; +const char* kIntentDisposition = "disposition"; const char* kIsolation = "app.isolation"; const char* kJs = "js"; const char* kKeycode = "keyCode"; @@ -113,6 +118,8 @@ const char* kWebURLs = "app.urls"; namespace extension_manifest_values { const char* kIncognitoSplit = "split"; const char* kIncognitoSpanning = "spanning"; +const char* kIntentDispositionWindow = "window"; +const char* kIntentDispositionInline = "inline"; const char* kIsolatedStorage = "storage"; const char* kRunAtDocumentStart = "document_start"; const char* kRunAtDocumentEnd = "document_end"; @@ -226,6 +233,18 @@ const char* kInvalidInputComponentShortcutKeycode = "Invalid value for 'input_conponents[*].shortcutKey.keyCode"; const char* kInvalidInputComponentType = "Invalid value for 'input_conponents[*].type"; +const char* kInvalidIntent = + "Invalid value for intents[*]"; +const char* kInvalidIntentDisposition = + "Invalid value for intents[*].disposition"; +const char* kInvalidIntentPath = + "Invalid value for intents[*].path"; +const char* kInvalidIntents = + "Invalid value for intents"; +const char* kInvalidIntentType = + "Invalid value for intents[*].type"; +const char* kInvalidIntentTitle = + "Invalid value for intents[*].title"; const char* kInvalidIsolation = "Invalid value for 'app.isolation'."; const char* kInvalidIsolationValue = diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index ba14dd5..8c52a33 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -39,6 +39,11 @@ namespace extension_manifest_keys { extern const char* kIncognito; extern const char* kIncludeGlobs; extern const char* kInputComponents; + extern const char* kIntents; + extern const char* kIntentType; + extern const char* kIntentPath; + extern const char* kIntentTitle; + extern const char* kIntentDisposition; extern const char* kIsolation; extern const char* kJs; extern const char* kKeycode; @@ -115,6 +120,8 @@ namespace extension_manifest_keys { namespace extension_manifest_values { extern const char* kIncognitoSplit; extern const char* kIncognitoSpanning; + extern const char* kIntentDispositionWindow; + extern const char* kIntentDispositionInline; extern const char* kIsolatedStorage; extern const char* kLaunchContainerPanel; extern const char* kLaunchContainerTab; @@ -176,6 +183,12 @@ namespace extension_manifest_errors { extern const char* kInvalidInputComponentShortcutKey; extern const char* kInvalidInputComponentShortcutKeycode; extern const char* kInvalidInputComponentType; + extern const char* kInvalidIntent; + extern const char* kInvalidIntentDisposition; + extern const char* kInvalidIntentPath; + extern const char* kInvalidIntents; + extern const char* kInvalidIntentType; + extern const char* kInvalidIntentTitle; extern const char* kInvalidIsolation; extern const char* kInvalidIsolationValue; extern const char* kInvalidJs; diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index 3d7ed44..7d89eae 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -747,6 +747,51 @@ TEST_F(ExtensionManifestTest, TtsEngine) { EXPECT_EQ(3U, extension->tts_voices()[0].event_types.size()); } +TEST_F(ExtensionManifestTest, WebIntents) { + CommandLine::ForCurrentProcess()->AppendSwitch("--enable-web-intents"); + + LoadAndExpectError("intent_invalid_1.json", + extension_manifest_errors::kInvalidIntents); + LoadAndExpectError("intent_invalid_2.json", + extension_manifest_errors::kInvalidIntent); + LoadAndExpectError("intent_invalid_3.json", + extension_manifest_errors::kInvalidIntentPath); + LoadAndExpectError("intent_invalid_4.json", + extension_manifest_errors::kInvalidIntentDisposition); + LoadAndExpectError("intent_invalid_5.json", + extension_manifest_errors::kInvalidIntentType); + LoadAndExpectError("intent_invalid_6.json", + extension_manifest_errors::kInvalidIntentTitle); + + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("intent_valid.json")); + ASSERT_TRUE(extension.get() != NULL); + + ASSERT_EQ(1u, extension->intents().size()); + EXPECT_EQ("image/png", UTF16ToUTF8(extension->intents()[0].type)); + EXPECT_EQ("http://webintents.org/share", + UTF16ToUTF8(extension->intents()[0].action)); + EXPECT_EQ("chrome-extension", extension->intents()[0].service_url.scheme()); + EXPECT_EQ("///services/share", extension->intents()[0].service_url.path()); + EXPECT_EQ("Sample Sharing Intent", + UTF16ToUTF8(extension->intents()[0].title)); + EXPECT_EQ(WebIntentServiceData::DISPOSITION_INLINE, + extension->intents()[0].disposition); + + // Verify that optional fields are filled with defaults. + extension = LoadAndExpectSuccess("intent_valid_minimal.json"); + ASSERT_TRUE(extension.get() != NULL); + + ASSERT_EQ(1u, extension->intents().size()); + EXPECT_EQ("", UTF16ToUTF8(extension->intents()[0].type)); + EXPECT_EQ("http://webintents.org/share", + UTF16ToUTF8(extension->intents()[0].action)); + EXPECT_TRUE(extension->intents()[0].service_url.is_empty()); + EXPECT_EQ("", UTF16ToUTF8(extension->intents()[0].title)); + EXPECT_EQ(WebIntentServiceData::DISPOSITION_WINDOW, + extension->intents()[0].disposition); +} + TEST_F(ExtensionManifestTest, ForbidPortsInPermissions) { // Loading as a user would shoud not trigger an error. LoadAndExpectSuccess("forbid_ports_in_permissions.json"); diff --git a/chrome/test/data/extensions/manifest_tests/intent_invalid_1.json b/chrome/test/data/extensions/manifest_tests/intent_invalid_1.json new file mode 100644 index 0000000..ed16b37 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_invalid_1.json @@ -0,0 +1,5 @@ +{ + "name": "test", + "version": "1", + "intents": "shouldBeADict" +} diff --git a/chrome/test/data/extensions/manifest_tests/intent_invalid_2.json b/chrome/test/data/extensions/manifest_tests/intent_invalid_2.json new file mode 100644 index 0000000..b3c1850 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_invalid_2.json @@ -0,0 +1,7 @@ +{ + "name": "test", + "version": "1", + "intents": { + "http://webintents.org/" : "shouldBeADict" + } +} diff --git a/chrome/test/data/extensions/manifest_tests/intent_invalid_3.json b/chrome/test/data/extensions/manifest_tests/intent_invalid_3.json new file mode 100644 index 0000000..72f04c2 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_invalid_3.json @@ -0,0 +1,9 @@ +{ + "name": "test", + "version": "1", + "intents": { + "http://webintents.org/intent/share" : { + "path" : { "shouldBeA" : "string" } + } + } +} diff --git a/chrome/test/data/extensions/manifest_tests/intent_invalid_4.json b/chrome/test/data/extensions/manifest_tests/intent_invalid_4.json new file mode 100644 index 0000000..dfb2d51 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_invalid_4.json @@ -0,0 +1,9 @@ +{ + "name": "test", + "version": "1", + "intents": { + "http://webintents.org/intent/share" : { + "disposition" : "gibberish" + } + } +} diff --git a/chrome/test/data/extensions/manifest_tests/intent_invalid_5.json b/chrome/test/data/extensions/manifest_tests/intent_invalid_5.json new file mode 100644 index 0000000..6c0d5fc --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_invalid_5.json @@ -0,0 +1,9 @@ +{ + "name": "test", + "version": "1", + "intents": { + "http://webintents.org/intent/share" : { + "type" : { "shouldBeA" : "string" } + } + } +} diff --git a/chrome/test/data/extensions/manifest_tests/intent_invalid_6.json b/chrome/test/data/extensions/manifest_tests/intent_invalid_6.json new file mode 100644 index 0000000..585c408 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_invalid_6.json @@ -0,0 +1,9 @@ +{ + "name": "test", + "version": "1", + "intents": { + "http://webintents.org/intent/share" : { + "title" : { "shouldBeA" : "string" } + } + } +} diff --git a/chrome/test/data/extensions/manifest_tests/intent_valid.json b/chrome/test/data/extensions/manifest_tests/intent_valid.json new file mode 100644 index 0000000..9a62df8 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_valid.json @@ -0,0 +1,12 @@ +{ + "name": "test", + "version": "1", + "intents": { + "http://webintents.org/share" : { + "type" : "image/png", + "path" : "//services/share", + "title" : "Sample Sharing Intent", + "disposition" : "inline" + } + } +} diff --git a/chrome/test/data/extensions/manifest_tests/intent_valid_minimal.json b/chrome/test/data/extensions/manifest_tests/intent_valid_minimal.json new file mode 100644 index 0000000..22a9508 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/intent_valid_minimal.json @@ -0,0 +1,8 @@ +{ + "name": "test", + "version": "1", + "intents": { + "http://webintents.org/share" : { + } + } +} |