summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjennyz <jennyz@chromium.org>2015-09-03 13:59:27 -0700
committerCommit bot <commit-bot@chromium.org>2015-09-03 21:00:03 +0000
commit26b7f4356b905831f152cebb28c08a16ad72d80c (patch)
tree3a0acf791ae608ab0601bfa412405ee82c9e933a
parentfb833eca8993b1210f530fcfa09cd3e72d2d45fe (diff)
downloadchromium_src-26b7f4356b905831f152cebb28c08a16ad72d80c.zip
chromium_src-26b7f4356b905831f152cebb28c08a16ad72d80c.tar.gz
chromium_src-26b7f4356b905831f152cebb28c08a16ad72d80c.tar.bz2
Implement kiosk multiple apps feature. This includes:
1. Add new manifest keys in the manifest file of the primary kiosk app to configure secondary kiosk apps. 2. Implement the work flow for primary kiosk to download and install secondary apps before its first time launch. 3. Expand FakeCWS to support multiple kiosk apps' update check. 4. Move the hard coded local extension cache director to chromeos path service so that it can be created in testing environment. BUG=510217 TBR=stevenjb Review URL: https://codereview.chromium.org/1301323005 Cr-Commit-Position: refs/heads/master@{#347237}
-rw-r--r--chrome/browser/chromeos/app_mode/fake_cws.cc136
-rw-r--r--chrome/browser/chromeos/app_mode/fake_cws.h16
-rw-r--r--chrome/browser/chromeos/app_mode/kiosk_app_manager.cc38
-rw-r--r--chrome/browser/chromeos/app_mode/kiosk_app_manager.h20
-rw-r--r--chrome/browser/chromeos/app_mode/startup_app_launcher.cc113
-rw-r--r--chrome/browser/chromeos/app_mode/startup_app_launcher.h12
-rw-r--r--chrome/browser/chromeos/login/kiosk_browsertest.cc149
-rw-r--r--chrome/browser/extensions/external_provider_impl.cc19
-rw-r--r--chrome/browser/extensions/updater/chromeos_extension_cache_delegate.cc12
-rw-r--r--chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h2
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/app_main.html5
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-128.pngbin0 -> 343 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-16.pngbin0 -> 94 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/main.js11
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/manifest.json24
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/app_main.html5
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-128.pngbin0 -> 343 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-16.pngbin0 -> 94 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/main.js11
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/manifest.json23
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/app_main.html5
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-128.pngbin0 -> 343 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-16.pngbin0 -> 94 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/main.js11
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/manifest.json25
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/app_main.html5
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-128.pngbin0 -> 343 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-16.pngbin0 -> 94 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/main.js11
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/manifest.json20
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/app_main.html5
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-128.pngbin0 -> 343 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-16.pngbin0 -> 94 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/main.js11
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/manifest.json20
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/app_main.html5
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-128.pngbin0 -> 343 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-16.pngbin0 -> 94 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/main.js11
-rw-r--r--chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/manifest.json20
-rw-r--r--chrome/test/data/chromeos/app_mode/webstore/downloads/aabnpdpieclcikafhdkkpldcaodmfoai-1.0.0.crxbin0 -> 3465 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-1.0.0.crxbin0 -> 3511 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-2.0.0-1app.crxbin0 -> 3486 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-3.0.0-3app.crxbin0 -> 3537 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/webstore/downloads/fiehokkcgaojmbhfhlpiheggjhaedjoc-1.0.0.crxbin0 -> 3467 bytes
-rw-r--r--chrome/test/data/chromeos/app_mode/webstore/downloads/ihplaomghjbeafnpnjkhppmfpnmdihgd-1.0.0.crxbin0 -> 3463 bytes
-rw-r--r--chromeos/chromeos_paths.cc9
-rw-r--r--chromeos/chromeos_paths.h2
-rw-r--r--extensions/common/api/_manifest_features.json6
-rw-r--r--extensions/common/api/extensions_manifest_types.json16
-rw-r--r--extensions/common/manifest_constants.cc7
-rw-r--r--extensions/common/manifest_constants.h4
-rw-r--r--extensions/common/manifest_handlers/kiosk_mode_info.cc54
-rw-r--r--extensions/common/manifest_handlers/kiosk_mode_info.h15
-rw-r--r--extensions/common/manifest_handlers/kiosk_mode_info_unittest.cc35
-rw-r--r--extensions/extensions_tests.gypi1
-rw-r--r--extensions/test/data/manifest_tests/kiosk_secondary_app_multi_apps.json24
-rw-r--r--extensions/test/data/manifest_tests/kiosk_secondary_app_no_secondary_app.json20
-rw-r--r--tools/json_schema_compiler/h_generator.py2
59 files changed, 839 insertions, 101 deletions
diff --git a/chrome/browser/chromeos/app_mode/fake_cws.cc b/chrome/browser/chromeos/app_mode/fake_cws.cc
index 401e3f2..e8c798d 100644
--- a/chrome/browser/chromeos/app_mode/fake_cws.cc
+++ b/chrome/browser/chromeos/app_mode/fake_cws.cc
@@ -14,6 +14,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "crypto/sha2.h"
+#include "net/base/url_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
using net::test_server::BasicHttpResponse;
@@ -29,6 +30,44 @@ const char kWebstoreDomain[] = "cws.com";
// Kiosk app crx file download path under web store site.
const char kCrxDownloadPath[] = "/chromeos/app_mode/webstore/downloads/";
+const char kAppNoUpdateTemplate[] =
+ "<app appid=\"$AppId\" status=\"ok\">"
+ "<updatecheck status=\"noupdate\"/>"
+ "</app>";
+
+const char kAppHasUpdateTemplate[] =
+ "<app appid=\"$AppId\" status=\"ok\">"
+ "<updatecheck codebase=\"$CrxDownloadUrl\" fp=\"1.$FP\" "
+ "hash=\"\" hash_sha256=\"$FP\" size=\"$Size\" status=\"ok\" "
+ "version=\"$Version\"/>"
+ "</app>";
+
+const char kPrivateStoreAppHasUpdateTemplate[] =
+ "<app appid=\"$AppId\">"
+ "<updatecheck codebase=\"$CrxDownloadUrl\" version=\"$Version\"/>"
+ "</app>";
+
+const char kUpdateContentTemplate[] =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<gupdate xmlns=\"http://www.google.com/update2/response\" "
+ "protocol=\"2.0\" server=\"prod\">"
+ "<daystart elapsed_days=\"2569\" elapsed_seconds=\"36478\"/>"
+ "$APPS"
+ "</gupdate>";
+
+bool GetAppIdsFromUpdateUrl(const GURL& update_url,
+ std::vector<std::string>* ids) {
+ for (net::QueryIterator it(update_url); !it.IsAtEnd(); it.Advance()) {
+ if (it.GetKey() != "x")
+ continue;
+ std::string id;
+ net::GetValueForKeyInQuery(GURL("http://dummy?" + it.GetUnescapedValue()),
+ "id", &id);
+ ids->push_back(id);
+ }
+ return !ids->empty();
+}
+
} // namespace
FakeCWS::FakeCWS() : update_check_count_(0) {
@@ -38,9 +77,8 @@ FakeCWS::~FakeCWS() {
}
void FakeCWS::Init(EmbeddedTestServer* embedded_test_server) {
- has_update_template_ =
- "chromeos/app_mode/webstore/update_check/has_update.xml";
- no_update_template_ = "chromeos/app_mode/webstore/update_check/no_update.xml";
+ has_update_template_ = kAppHasUpdateTemplate;
+ no_update_template_ = kAppNoUpdateTemplate;
update_check_end_point_ = "/update_check.xml";
SetupWebStoreURL(embedded_test_server->base_url());
@@ -51,9 +89,8 @@ void FakeCWS::Init(EmbeddedTestServer* embedded_test_server) {
void FakeCWS::InitAsPrivateStore(EmbeddedTestServer* embedded_test_server,
const std::string& update_check_end_point) {
- has_update_template_ =
- "chromeos/app_mode/webstore/update_check/has_update_private_store.xml";
- no_update_template_ = "chromeos/app_mode/webstore/update_check/no_update.xml";
+ has_update_template_ = kPrivateStoreAppHasUpdateTemplate;
+ no_update_template_ = kAppNoUpdateTemplate;
update_check_end_point_ = update_check_end_point;
SetupWebStoreURL(embedded_test_server->base_url());
@@ -77,24 +114,25 @@ void FakeCWS::SetUpdateCrx(const std::string& app_id,
const std::string sha256 = crypto::SHA256HashString(crx_content);
const std::string sha256_hex = base::HexEncode(sha256.c_str(), sha256.size());
- SetUpdateCheckContent(
- has_update_template_,
- crx_download_url,
- app_id,
- sha256_hex,
- base::UintToString(crx_content.size()),
- version,
- &update_check_content_);
+ std::string update_check_content(has_update_template_);
+ base::ReplaceSubstringsAfterOffset(&update_check_content, 0, "$AppId",
+ app_id);
+ base::ReplaceSubstringsAfterOffset(
+ &update_check_content, 0, "$CrxDownloadUrl", crx_download_url.spec());
+ base::ReplaceSubstringsAfterOffset(&update_check_content, 0, "$FP",
+ sha256_hex);
+ base::ReplaceSubstringsAfterOffset(&update_check_content, 0, "$Size",
+ base::UintToString(crx_content.size()));
+ base::ReplaceSubstringsAfterOffset(&update_check_content, 0, "$Version",
+ version);
+ id_to_update_check_content_map_[app_id] = update_check_content;
}
void FakeCWS::SetNoUpdate(const std::string& app_id) {
- SetUpdateCheckContent(no_update_template_,
- GURL(),
- app_id,
- "",
- "",
- "",
- &update_check_content_);
+ std::string app_update_check_content(no_update_template_);
+ base::ReplaceSubstringsAfterOffset(&app_update_check_content, 0, "$AppId",
+ app_id);
+ id_to_update_check_content_map_[app_id] = app_update_check_content;
}
int FakeCWS::GetUpdateCheckCountAndReset() {
@@ -128,40 +166,42 @@ void FakeCWS::OverrideGalleryCommandlineSwitches() {
update_url.spec());
}
-void FakeCWS::SetUpdateCheckContent(const std::string& update_check_file,
- const GURL& crx_download_url,
- const std::string& app_id,
- const std::string& crx_fp,
- const std::string& crx_size,
- const std::string& version,
+bool FakeCWS::GetUpdateCheckContent(const std::vector<std::string>& ids,
std::string* update_check_content) {
- base::FilePath test_data_dir;
- PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
- base::FilePath update_file =
- test_data_dir.AppendASCII(update_check_file.c_str());
- ASSERT_TRUE(base::ReadFileToString(update_file, update_check_content));
+ std::string apps_content;
+ for (const std::string& id : ids) {
+ std::string app_update_content;
+ auto it = id_to_update_check_content_map_.find(id);
+ if (it == id_to_update_check_content_map_.end())
+ return false;
+ apps_content.append(it->second);
+ }
+ if (apps_content.empty())
+ return false;
- base::ReplaceSubstringsAfterOffset(update_check_content, 0, "$AppId", app_id);
- base::ReplaceSubstringsAfterOffset(
- update_check_content, 0, "$CrxDownloadUrl", crx_download_url.spec());
- base::ReplaceSubstringsAfterOffset(update_check_content, 0, "$FP", crx_fp);
- base::ReplaceSubstringsAfterOffset(update_check_content, 0,
- "$Size", crx_size);
- base::ReplaceSubstringsAfterOffset(update_check_content, 0,
- "$Version", version);
+ *update_check_content = kUpdateContentTemplate;
+ base::ReplaceSubstringsAfterOffset(update_check_content, 0, "$APPS",
+ apps_content);
+ return true;
}
scoped_ptr<HttpResponse> FakeCWS::HandleRequest(const HttpRequest& request) {
GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
std::string request_path = request_url.path();
- if (!update_check_content_.empty() &&
- request_path.find(update_check_end_point_) != std::string::npos) {
- ++update_check_count_;
- scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
- http_response->set_code(net::HTTP_OK);
- http_response->set_content_type("text/xml");
- http_response->set_content(update_check_content_);
- return http_response.Pass();
+ if (request_path.find(update_check_end_point_) != std::string::npos &&
+ !id_to_update_check_content_map_.empty()) {
+ std::vector<std::string> ids;
+ if (GetAppIdsFromUpdateUrl(request_url, &ids)) {
+ std::string update_check_content;
+ if (GetUpdateCheckContent(ids, &update_check_content)) {
+ ++update_check_count_;
+ scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
+ http_response->set_code(net::HTTP_OK);
+ http_response->set_content_type("text/xml");
+ http_response->set_content(update_check_content);
+ return http_response.Pass();
+ }
+ }
}
return scoped_ptr<HttpResponse>();
diff --git a/chrome/browser/chromeos/app_mode/fake_cws.h b/chrome/browser/chromeos/app_mode/fake_cws.h
index de25896..baade20 100644
--- a/chrome/browser/chromeos/app_mode/fake_cws.h
+++ b/chrome/browser/chromeos/app_mode/fake_cws.h
@@ -5,6 +5,10 @@
#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_FAKE_CWS_H_
#define CHROME_BROWSER_CHROMEOS_APP_MODE_FAKE_CWS_H_
+#include <map>
+#include <string>
+#include <vector>
+
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
@@ -47,14 +51,7 @@ class FakeCWS {
void SetupWebStoreURL(const GURL& test_server_url);
void OverrideGalleryCommandlineSwitches();
- // Sets up |update_check_content_| used in update request response later by
- // kiosk app update server request handler |HandleRequest|.
- void SetUpdateCheckContent(const std::string& update_check_file,
- const GURL& crx_download_url,
- const std::string& app_id,
- const std::string& crx_fp,
- const std::string& crx_size,
- const std::string& version,
+ bool GetUpdateCheckContent(const std::vector<std::string>& ids,
std::string* update_check_content);
// Request handler for kiosk app update server.
@@ -67,7 +64,8 @@ class FakeCWS {
std::string no_update_template_;
std::string update_check_end_point_;
- std::string update_check_content_;
+ // Map keyed by app_id to app_update_content.
+ std::map<std::string, std::string> id_to_update_check_content_map_;
int update_check_count_;
DISALLOW_COPY_AND_ASSIGN(FakeCWS);
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
index a37df89..d590ff3 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
@@ -465,7 +465,7 @@ void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
extensions::ExternalLoader* KioskAppManager::CreateExternalLoader() {
if (external_loader_created_) {
NOTREACHED();
- return NULL;
+ return nullptr;
}
external_loader_created_ = true;
KioskAppExternalLoader* loader = new KioskAppExternalLoader();
@@ -474,6 +474,19 @@ extensions::ExternalLoader* KioskAppManager::CreateExternalLoader() {
return loader;
}
+extensions::ExternalLoader*
+KioskAppManager::CreateSecondaryAppExternalLoader() {
+ if (secondary_app_external_loader_created_) {
+ NOTREACHED();
+ return nullptr;
+ }
+ secondary_app_external_loader_created_ = true;
+ KioskAppExternalLoader* secondary_loader = new KioskAppExternalLoader();
+ secondary_app_external_loader_ = secondary_loader->AsWeakPtr();
+
+ return secondary_loader;
+}
+
void KioskAppManager::InstallFromCache(const std::string& id) {
const base::DictionaryValue* extension = NULL;
if (external_cache_->cached_extensions()->GetDictionary(id, &extension)) {
@@ -487,6 +500,25 @@ void KioskAppManager::InstallFromCache(const std::string& id) {
}
}
+void KioskAppManager::InstallSecondaryApps(
+ const std::vector<std::string>& ids) {
+ if (ids.empty())
+ return;
+
+ scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
+ for (const std::string& id : ids) {
+ scoped_ptr<base::DictionaryValue> extension_entry(
+ new base::DictionaryValue);
+ extension_entry->SetStringWithoutPathExpansion(
+ extensions::ExternalProviderImpl::kExternalUpdateUrl,
+ extension_urls::GetWebstoreUpdateUrl().spec());
+ extension_entry->SetBoolean(
+ extensions::ExternalProviderImpl::kIsFromWebstore, true);
+ prefs->Set(id, extension_entry.Pass());
+ }
+ secondary_app_external_loader_->SetCurrentAppExtensions(prefs.Pass());
+}
+
void KioskAppManager::UpdateExternalCache() {
UpdateAppData();
}
@@ -511,7 +543,9 @@ void KioskAppManager::PutValidatedExternalExtension(
}
KioskAppManager::KioskAppManager()
- : ownership_established_(false), external_loader_created_(false) {
+ : ownership_established_(false),
+ external_loader_created_(false),
+ secondary_app_external_loader_created_(false) {
base::FilePath cache_dir;
GetCrxCacheDir(&cache_dir);
external_cache_.reset(
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
index ae7cbab..90e020a 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
@@ -176,13 +176,20 @@ class KioskAppManager : public KioskAppDataDelegate,
void AddObserver(KioskAppManagerObserver* observer);
void RemoveObserver(KioskAppManagerObserver* observer);
- // Creates extensions::ExternalLoader for installing kiosk apps during their
- // first time launch.
+ // Creates extensions::ExternalLoader for installing the primary kiosk app
+ // during its first time launch.
extensions::ExternalLoader* CreateExternalLoader();
+ // Creates extensions::ExternalLoader for installing secondary kiosk apps
+ // before launching the primary app for the first time.
+ extensions::ExternalLoader* CreateSecondaryAppExternalLoader();
+
// Installs kiosk app with |id| from cache.
void InstallFromCache(const std::string& id);
+ // Installs the secondary apps listed in |ids|.
+ void InstallSecondaryApps(const std::vector<std::string>& ids);
+
void UpdateExternalCache();
// Monitors kiosk external update from usb stick.
@@ -203,6 +210,9 @@ class KioskAppManager : public KioskAppDataDelegate,
const ExternalCache::PutExternalExtensionCallback& callback);
bool external_loader_created() const { return external_loader_created_; }
+ bool secondary_app_external_loader_created() const {
+ return secondary_app_external_loader_created_;
+ }
// Notifies the KioskAppManager that a given app was auto-launched
// automatically with no delay on startup. Certain privacy-sensitive
@@ -288,10 +298,14 @@ class KioskAppManager : public KioskAppDataDelegate,
scoped_ptr<KioskExternalUpdater> usb_stick_updater_;
- // The extension external loader for installing kiosk app.
+ // The extension external loader for deploying primary app.
bool external_loader_created_;
base::WeakPtr<KioskAppExternalLoader> external_loader_;
+ // The extension external loader for deploying secondary apps.
+ bool secondary_app_external_loader_created_;
+ base::WeakPtr<KioskAppExternalLoader> secondary_app_external_loader_;
+
DISALLOW_COPY_AND_ASSIGN(KioskAppManager);
};
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
index fdd7bc5..cb2bf6d 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
@@ -178,8 +178,7 @@ void StartupAppLauncher::RestartLauncher() {
void StartupAppLauncher::MaybeInitializeNetwork() {
network_ready_handled_ = false;
- const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
- extension_service()->GetInstalledExtension(app_id_);
+ const Extension* extension = GetPrimaryAppExtension();
bool crx_cached = KioskAppManager::Get()->HasCachedCrx(app_id_);
const bool requires_network =
(!extension && !crx_cached) ||
@@ -247,9 +246,7 @@ void StartupAppLauncher::OnRefreshTokensLoaded() {
void StartupAppLauncher::MaybeLaunchApp() {
// Check if the app is offline enabled.
- const Extension* extension = extensions::ExtensionSystem::Get(profile_)
- ->extension_service()
- ->GetInstalledExtension(app_id_);
+ const Extension* extension = GetPrimaryAppExtension();
DCHECK(extension);
const bool offline_enabled =
extensions::OfflineEnabledInfo::IsOfflineEnabled(extension);
@@ -289,14 +286,23 @@ void StartupAppLauncher::OnFinishCrxInstall(const std::string& extension_id,
return;
}
- if (success) {
- MaybeLaunchApp();
+ if (extension_id == app_id_) {
+ if (success) {
+ MaybeInstallSecondaryApps();
+ } else {
+ LOG(ERROR) << "Failed to install the kiosk application app_id: "
+ << extension_id;
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+ }
return;
}
- LOG(ERROR) << "Failed to install the kiosk application app_id: "
- << extension_id;
- OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+ if (AreSecondaryAppsInstalled()) {
+ MaybeLaunchApp();
+ } else {
+ LOG(ERROR) << "Failed to install one or more secondary apps.";
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+ }
}
void StartupAppLauncher::OnKioskExtensionLoadedInCache(
@@ -321,14 +327,57 @@ void StartupAppLauncher::OnKioskAppDataLoadStatusChanged(
OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_DOWNLOAD);
}
+bool StartupAppLauncher::IsAnySecondaryAppPending() const {
+ DCHECK(HasSecondaryApps());
+ const extensions::Extension* extension = GetPrimaryAppExtension();
+ DCHECK(extension);
+ extensions::KioskModeInfo* info = extensions::KioskModeInfo::Get(extension);
+ for (const auto& id : info->secondary_app_ids) {
+ if (extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->pending_extension_manager()
+ ->IsIdPending(id)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool StartupAppLauncher::AreSecondaryAppsInstalled() const {
+ DCHECK(HasSecondaryApps());
+ const extensions::Extension* extension = GetPrimaryAppExtension();
+ DCHECK(extension);
+ extensions::KioskModeInfo* info = extensions::KioskModeInfo::Get(extension);
+ for (const auto& id : info->secondary_app_ids) {
+ if (!extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->GetInstalledExtension(id)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool StartupAppLauncher::HasSecondaryApps() const {
+ const extensions::Extension* extension = GetPrimaryAppExtension();
+ DCHECK(extension);
+ return extensions::KioskModeInfo::HasSecondaryApps(extension);
+}
+
+const extensions::Extension* StartupAppLauncher::GetPrimaryAppExtension()
+ const {
+ return extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->GetInstalledExtension(app_id_);
+}
+
void StartupAppLauncher::LaunchApp() {
if (!ready_to_launch_) {
NOTREACHED();
LOG(ERROR) << "LaunchApp() called but launcher is not initialized.";
}
- const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
- extension_service()->GetInstalledExtension(app_id_);
+ const Extension* extension = GetPrimaryAppExtension();
CHECK(extension);
if (!extensions::KioskModeInfo::IsKioskEnabled(extension)) {
@@ -380,17 +429,47 @@ void StartupAppLauncher::BeginInstall() {
return;
}
- if (extensions::ExtensionSystem::Get(profile_)
- ->extension_service()
- ->GetInstalledExtension(app_id_)) {
- // Launch the app.
- OnReadyToLaunch();
+ if (GetPrimaryAppExtension()) {
+ // Install secondary apps.
+ MaybeInstallSecondaryApps();
} else {
// The extension is skipped for installation due to some error.
OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
}
}
+void StartupAppLauncher::MaybeInstallSecondaryApps() {
+ if (!HasSecondaryApps()) {
+ // Launch the primary app.
+ MaybeLaunchApp();
+ return;
+ }
+
+ if (!AreSecondaryAppsInstalled() && !delegate_->IsNetworkReady()) {
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+ return;
+ }
+
+ extensions::KioskModeInfo* info =
+ extensions::KioskModeInfo::Get(GetPrimaryAppExtension());
+ KioskAppManager::Get()->InstallSecondaryApps(info->secondary_app_ids);
+ if (IsAnySecondaryAppPending()) {
+ delegate_->OnInstallingApp();
+ // Observe the crx installation events.
+ extensions::InstallTracker* tracker =
+ extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+ tracker->AddObserver(this);
+ return;
+ }
+
+ if (AreSecondaryAppsInstalled()) {
+ // Launch the primary app.
+ MaybeLaunchApp();
+ } else {
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+ }
+}
+
void StartupAppLauncher::OnReadyToLaunch() {
ready_to_launch_ = true;
UpdateAppData();
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.h b/chrome/browser/chromeos/app_mode/startup_app_launcher.h
index 4133b4a..4f8a1de 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.h
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.h
@@ -91,6 +91,7 @@ class StartupAppLauncher : public base::SupportsWeakPtr<StartupAppLauncher>,
void InitializeTokenService();
void MaybeInitializeNetwork();
+ void MaybeInstallSecondaryApps();
void MaybeLaunchApp();
void StartLoadingOAuthFile();
@@ -99,6 +100,17 @@ class StartupAppLauncher : public base::SupportsWeakPtr<StartupAppLauncher>,
void OnKioskAppDataLoadStatusChanged(const std::string& app_id);
+ // Returns true if any secondary app is pending.
+ bool IsAnySecondaryAppPending() const;
+
+ // Returns true if all secondary apps have been installed.
+ bool AreSecondaryAppsInstalled() const;
+
+ // Returns true if secondary apps are declared in manifest.
+ bool HasSecondaryApps() const;
+
+ const extensions::Extension* GetPrimaryAppExtension() const;
+
// OAuth2TokenService::Observer overrides.
void OnRefreshTokenAvailable(const std::string& account_id) override;
void OnRefreshTokensLoaded() override;
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index 22a2b6e..06d50e5 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -43,6 +43,7 @@
#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/updater/local_extension_cache.h"
#include "chrome/browser/profiles/profile_impl.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profiles_state.h"
@@ -51,6 +52,7 @@
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
+#include "chromeos/chromeos_paths.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/disks/disk_mount_manager.h"
@@ -118,6 +120,12 @@ const char kTestLocalFsKioskApp[] = "bmbpicmpniaclbbpdkfglgipkkebnbjf";
// detail/aaedpojejpghjkedenggihopfhfijcko
const char kTestGetVolumeListKioskApp[] = "aaedpojejpghjkedenggihopfhfijcko";
+// Testing apps for testing kiosk multi-app feature.
+const char kTestPrimaryKioskApp[] = "ceobkcclegcliomogfoeoheahogoecgl";
+const char kTestSecondaryApp1[] = "ihplaomghjbeafnpnjkhppmfpnmdihgd";
+const char kTestSecondaryApp2[] = "fiehokkcgaojmbhfhlpiheggjhaedjoc";
+const char kTestSecondaryApp3[] = "aabnpdpieclcikafhdkkpldcaodmfoai";
+
// Fake usb stick mount path.
const char kFakeUsbMountPathUpdatePass[] =
"chromeos/app_mode/external_update/update_pass";
@@ -189,6 +197,16 @@ void LockAndUnlock(scoped_ptr<base::Lock> lock) {
lock->Release();
}
+bool IsAppInstalled(const std::string app_id) {
+ Profile* app_profile = ProfileManager::GetPrimaryUserProfile();
+ DCHECK(app_profile);
+ const extensions::Extension* app =
+ extensions::ExtensionSystem::Get(app_profile)
+ ->extension_service()
+ ->GetInstalledExtension(app_id);
+ return app != nullptr;
+}
+
// Helper functions for CanConfigureNetwork mock.
class ScopedCanConfigureNetwork {
public:
@@ -1247,6 +1265,18 @@ class KioskUpdateTest : public KioskTest {
KioskUpdateTest() {}
~KioskUpdateTest() override {}
+ struct TestAppInfo {
+ std::string id;
+ std::string version;
+ std::string crx_filename;
+ TestAppInfo() {}
+ TestAppInfo(const std::string& id,
+ const std::string& version,
+ const std::string& crx_filename)
+ : id(id), version(version), crx_filename(crx_filename) {}
+ ~TestAppInfo() {}
+ };
+
protected:
void SetUp() override {
fake_disk_mount_manager_ = new KioskFakeDiskMountManager();
@@ -1267,6 +1297,9 @@ class KioskUpdateTest : public KioskTest {
// the app manager would accept existing files in its extension cache on the
// next startup) and copy the list to our stub settings provider as well.
settings_helper_.CopyStoredValue(kAccountsPrefDeviceLocalAccounts);
+
+ CreateAndInitializeLocalCache();
+
KioskTest::SetUpOnMainThread();
}
@@ -1343,6 +1376,47 @@ class KioskUpdateTest : public KioskTest {
EXPECT_EQ(version, GetInstalledAppVersion().GetString());
}
+ void LaunchKioskWithSecondaryApps(
+ const TestAppInfo& primary_app,
+ const std::vector<TestAppInfo>& secondary_apps) {
+ // Pre-cache the primary app.
+ PreCacheApp(primary_app.id, primary_app.version, primary_app.crx_filename);
+
+ set_test_app_id(primary_app.id);
+ fake_cws()->SetNoUpdate(primary_app.id);
+ for (size_t i = 0; i < secondary_apps.size(); ++i) {
+ fake_cws()->SetUpdateCrx(secondary_apps[i].id,
+ secondary_apps[i].crx_filename,
+ secondary_apps[i].version);
+ }
+
+ // Launch the primary app.
+ StartUIForAppLaunch();
+ SimulateNetworkOnline();
+ LaunchApp(test_app_id(), false);
+ WaitForAppLaunchWithOptions(false, true);
+
+ // Verify the primary app and the secondary apps are all installed.
+ EXPECT_EQ(primary_app.version, GetInstalledAppVersion().GetString());
+ for (size_t i = 0; i < secondary_apps.size(); ++i)
+ EXPECT_TRUE(IsAppInstalled(secondary_apps[i].id));
+ }
+
+ void LaunchTestKioskAppWithTwoSecondaryApps() {
+ TestAppInfo primary_app(kTestPrimaryKioskApp, "1.0.0",
+ std::string(kTestPrimaryKioskApp) + "-1.0.0.crx");
+
+ std::vector<TestAppInfo> secondary_apps;
+ TestAppInfo secondary_app_1(kTestSecondaryApp1, "1.0.0",
+ std::string(kTestSecondaryApp1) + "-1.0.0.crx");
+ secondary_apps.push_back(secondary_app_1);
+ TestAppInfo secondary_app_2(kTestSecondaryApp2, "1.0.0",
+ std::string(kTestSecondaryApp2) + "-1.0.0.crx");
+ secondary_apps.push_back(secondary_app_2);
+
+ LaunchKioskWithSecondaryApps(primary_app, secondary_apps);
+ }
+
private:
class KioskAppExternalUpdateWaiter : public KioskAppManagerObserver {
public:
@@ -1395,6 +1469,19 @@ class KioskUpdateTest : public KioskTest {
DISALLOW_COPY_AND_ASSIGN(KioskAppExternalUpdateWaiter);
};
+ // The local cache is supposed to be initialized on chromeos device, and a
+ // ready flag file will be pre-created to mark the ready state, before chrome
+ // starts. In order for the tests to run without being on real chromeos
+ // device, we need to manually create this file.
+ void CreateAndInitializeLocalCache() {
+ base::FilePath extension_cache_dir;
+ CHECK(PathService::Get(chromeos::DIR_DEVICE_EXTENSION_LOCAL_CACHE,
+ &extension_cache_dir));
+ base::FilePath cache_init_file = extension_cache_dir.Append(
+ extensions::LocalExtensionCache::kCacheReadyFlagFileName);
+ EXPECT_EQ(base::WriteFile(cache_init_file, "", 0), 0);
+ }
+
// Owned by DiskMountManager.
KioskFakeDiskMountManager* fake_disk_mount_manager_;
@@ -1708,6 +1795,68 @@ IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PreserveLocalData) {
ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
}
+/* ***** Test Kiosk multi-app feature ***** */
+
+// Launch a primary kiosk app which has two secondary apps.
+IN_PROC_BROWSER_TEST_F(KioskUpdateTest,
+ LaunchTestKioskAppWithTwoSecondaryApps) {
+ LaunchTestKioskAppWithTwoSecondaryApps();
+}
+
+IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_UpdateMultiAppKioskRemoveOneApp) {
+ LaunchTestKioskAppWithTwoSecondaryApps();
+}
+
+// Update the primary app to version 2 which removes one of the secondary app
+// from its manifest.
+IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UpdateMultiAppKioskRemoveOneApp) {
+ set_test_app_id(kTestPrimaryKioskApp);
+ fake_cws()->SetUpdateCrx(
+ kTestPrimaryKioskApp,
+ std::string(kTestPrimaryKioskApp) + "-2.0.0-1app.crx", "2.0.0");
+ fake_cws()->SetNoUpdate(kTestSecondaryApp1);
+ fake_cws()->SetNoUpdate(kTestSecondaryApp2);
+
+ StartUIForAppLaunch();
+ SimulateNetworkOnline();
+ LaunchApp(test_app_id(), false);
+ WaitForAppLaunchWithOptions(false, true);
+
+ // Verify the secondary app kTestSecondaryApp1 is removed.
+ EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString());
+ EXPECT_FALSE(IsAppInstalled(kTestSecondaryApp1));
+ EXPECT_TRUE(IsAppInstalled(kTestSecondaryApp2));
+}
+
+IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_UpdateMultiAppKioskAddOneApp) {
+ LaunchTestKioskAppWithTwoSecondaryApps();
+}
+
+// Update the primary app to version 3 which adds a new secondary app in its
+// manifest.
+IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UpdateMultiAppKioskAddOneApp) {
+ set_test_app_id(kTestPrimaryKioskApp);
+ fake_cws()->SetUpdateCrx(
+ kTestPrimaryKioskApp,
+ std::string(kTestPrimaryKioskApp) + "-3.0.0-3app.crx", "3.0.0");
+ fake_cws()->SetNoUpdate(kTestSecondaryApp1);
+ fake_cws()->SetNoUpdate(kTestSecondaryApp2);
+ fake_cws()->SetUpdateCrx(kTestSecondaryApp3,
+ std::string(kTestSecondaryApp3) + "-1.0.0.crx",
+ "1.0.0");
+
+ StartUIForAppLaunch();
+ SimulateNetworkOnline();
+ LaunchApp(test_app_id(), false);
+ WaitForAppLaunchWithOptions(false, true);
+
+ // Verify the secondary app kTestSecondaryApp3 is installed.
+ EXPECT_EQ("3.0.0", GetInstalledAppVersion().GetString());
+ EXPECT_TRUE(IsAppInstalled(kTestSecondaryApp1));
+ EXPECT_TRUE(IsAppInstalled(kTestSecondaryApp2));
+ EXPECT_TRUE(IsAppInstalled(kTestSecondaryApp3));
+}
+
class KioskEnterpriseTest : public KioskTest {
protected:
KioskEnterpriseTest() {
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 7844bca..4a29cdb 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -491,8 +491,10 @@ void ExternalProviderImpl::CreateExternalProviders(
#if defined(OS_CHROMEOS)
chromeos::KioskAppManager* kiosk_app_manager =
chromeos::KioskAppManager::Get();
- DCHECK(kiosk_app_manager);
- if (kiosk_app_manager && !kiosk_app_manager->external_loader_created()) {
+ CHECK(kiosk_app_manager);
+
+ // Kiosk primary app external provider.
+ if (!kiosk_app_manager->external_loader_created()) {
scoped_ptr<ExternalProviderImpl> kiosk_app_provider(
new ExternalProviderImpl(
service, kiosk_app_manager->CreateExternalLoader(), profile,
@@ -503,6 +505,19 @@ void ExternalProviderImpl::CreateExternalProviders(
provider_list->push_back(
linked_ptr<ExternalProviderInterface>(kiosk_app_provider.release()));
}
+
+ // Kiosk secondary app external provider.
+ if (!kiosk_app_manager->secondary_app_external_loader_created()) {
+ scoped_ptr<ExternalProviderImpl> secondary_kiosk_app_provider(
+ new ExternalProviderImpl(
+ service, kiosk_app_manager->CreateSecondaryAppExternalLoader(),
+ profile, Manifest::EXTERNAL_PREF,
+ Manifest::EXTERNAL_PREF_DOWNLOAD, Extension::NO_FLAGS));
+ secondary_kiosk_app_provider->set_auto_acknowledge(true);
+ secondary_kiosk_app_provider->set_install_immediately(true);
+ provider_list->push_back(linked_ptr<ExternalProviderInterface>(
+ secondary_kiosk_app_provider.release()));
+ }
#endif
return;
}
diff --git a/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.cc b/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.cc
index b53d013..135dcb6 100644
--- a/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.cc
+++ b/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.cc
@@ -6,18 +6,16 @@
#include <stddef.h>
+#include "base/path_service.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chromeos/chromeos_paths.h"
#include "chromeos/settings/cros_settings_names.h"
namespace extensions {
-namespace {
-const char kLocalCacheDir[] = "/var/cache/external_cache";
-
-} // namespace
-
-ChromeOSExtensionCacheDelegate::ChromeOSExtensionCacheDelegate()
- : cache_dir_(kLocalCacheDir) {
+ChromeOSExtensionCacheDelegate::ChromeOSExtensionCacheDelegate() {
+ CHECK(PathService::Get(chromeos::DIR_DEVICE_EXTENSION_LOCAL_CACHE,
+ &cache_dir_));
}
ChromeOSExtensionCacheDelegate::ChromeOSExtensionCacheDelegate(
diff --git a/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h b/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h
index 5c6199c..8e9bf85 100644
--- a/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h
+++ b/chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h
@@ -22,7 +22,7 @@ class ChromeOSExtensionCacheDelegate : public ExtensionCacheDelegate {
size_t GetMaximumCacheSize() const override;
private:
- const base::FilePath cache_dir_;
+ base::FilePath cache_dir_;
DISALLOW_COPY_AND_ASSIGN(ChromeOSExtensionCacheDelegate);
};
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/app_main.html b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/app_main.html
new file mode 100644
index 0000000..0b7994f
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/app_main.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>Test Kiosk App, version: 1.0.0</h1>
+</body>
+<hmtl>
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-128.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-128.png
new file mode 100644
index 0000000..5c226f3
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-128.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-16.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-16.png
new file mode 100644
index 0000000..c7510d38
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/icon-16.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/main.js b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/main.js
new file mode 100644
index 0000000..2e23fdc
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/main.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.
+
+chrome.app.runtime.onLaunched.addListener(function(launchData) {
+ chrome.app.window.create('app_main.html',
+ { 'width': 1920,
+ 'height': 1080 },
+ function(window) {
+ });
+});
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/manifest.json b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/manifest.json
new file mode 100644
index 0000000..60e4b2c
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v1/manifest.json
@@ -0,0 +1,24 @@
+{
+ "manifest_version": 2,
+ "name": "Test Kiosk App for multi-apps",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "1.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "kiosk_secondary_apps": [
+ {"id": "ihplaomghjbeafnpnjkhppmfpnmdihgd"},
+ {"id": "fiehokkcgaojmbhfhlpiheggjhaedjoc"}
+ ],
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/app_main.html b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/app_main.html
new file mode 100644
index 0000000..204d074
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/app_main.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>Test Kiosk App, version: 2.0.0</h1>
+</body>
+<hmtl>
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-128.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-128.png
new file mode 100644
index 0000000..5c226f3
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-128.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-16.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-16.png
new file mode 100644
index 0000000..c7510d38
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/icon-16.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/main.js b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/main.js
new file mode 100644
index 0000000..15cb92d
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/main.js
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 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.
+
+chrome.app.runtime.onLaunched.addListener(function(launchData) {
+ chrome.app.window.create('app_main.html',
+ { 'width': 1920,
+ 'height': 1080 },
+ function(window) {
+ });
+});
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/manifest.json b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/manifest.json
new file mode 100644
index 0000000..55f7f6f
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v2/manifest.json
@@ -0,0 +1,23 @@
+{
+ "manifest_version": 2,
+ "name": "Test Kiosk App for multi-apps",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "2.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "kiosk_secondary_apps": [
+ {"id": "fiehokkcgaojmbhfhlpiheggjhaedjoc"}
+ ],
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/app_main.html b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/app_main.html
new file mode 100644
index 0000000..49bf040
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/app_main.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>Test Kiosk App, version: 3.0.0</h1>
+</body>
+<hmtl>
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-128.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-128.png
new file mode 100644
index 0000000..5c226f3
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-128.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-16.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-16.png
new file mode 100644
index 0000000..c7510d38
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/icon-16.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/main.js b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/main.js
new file mode 100644
index 0000000..15cb92d
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/main.js
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 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.
+
+chrome.app.runtime.onLaunched.addListener(function(launchData) {
+ chrome.app.window.create('app_main.html',
+ { 'width': 1920,
+ 'height': 1080 },
+ function(window) {
+ });
+});
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/manifest.json b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/manifest.json
new file mode 100644
index 0000000..0035ee3
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/primary_app/v3/manifest.json
@@ -0,0 +1,25 @@
+{
+ "manifest_version": 2,
+ "name": "Test Kiosk App for multi-apps",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "3.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "kiosk_secondary_apps": [
+ {"id": "ihplaomghjbeafnpnjkhppmfpnmdihgd"},
+ {"id": "fiehokkcgaojmbhfhlpiheggjhaedjoc"},
+ {"id": "aabnpdpieclcikafhdkkpldcaodmfoai"}
+ ],
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/app_main.html b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/app_main.html
new file mode 100644
index 0000000..00e8f84
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/app_main.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>Test Kiosk multi-app feature, secondary app 1, version: 1.0.0</h1>
+</body>
+<hmtl>
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-128.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-128.png
new file mode 100644
index 0000000..5c226f3
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-128.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-16.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-16.png
new file mode 100644
index 0000000..c7510d38
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/icon-16.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/main.js b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/main.js
new file mode 100644
index 0000000..15cb92d
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/main.js
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 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.
+
+chrome.app.runtime.onLaunched.addListener(function(launchData) {
+ chrome.app.window.create('app_main.html',
+ { 'width': 1920,
+ 'height': 1080 },
+ function(window) {
+ });
+});
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/manifest.json b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/manifest.json
new file mode 100644
index 0000000..fae3f22
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_1/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2,
+ "name": "Kiosk secondary app 1",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "1.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/app_main.html b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/app_main.html
new file mode 100644
index 0000000..35bdbcd
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/app_main.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>Test Kiosk multi-app feature, secondary app 2, version: 1.0.0</h1>
+</body>
+<hmtl>
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-128.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-128.png
new file mode 100644
index 0000000..5c226f3
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-128.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-16.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-16.png
new file mode 100644
index 0000000..c7510d38
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/icon-16.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/main.js b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/main.js
new file mode 100644
index 0000000..15cb92d
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/main.js
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 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.
+
+chrome.app.runtime.onLaunched.addListener(function(launchData) {
+ chrome.app.window.create('app_main.html',
+ { 'width': 1920,
+ 'height': 1080 },
+ function(window) {
+ });
+});
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/manifest.json b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/manifest.json
new file mode 100644
index 0000000..caef82d
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_2/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2,
+ "name": "Kiosk secondary app 2",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "1.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/app_main.html b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/app_main.html
new file mode 100644
index 0000000..3a4240c
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/app_main.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>Test Kiosk multi-app feature, secondary app 3, version: 1.0.0</h1>
+</body>
+<hmtl>
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-128.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-128.png
new file mode 100644
index 0000000..5c226f3
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-128.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-16.png b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-16.png
new file mode 100644
index 0000000..c7510d38
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/icon-16.png
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/main.js b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/main.js
new file mode 100644
index 0000000..15cb92d
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/main.js
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 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.
+
+chrome.app.runtime.onLaunched.addListener(function(launchData) {
+ chrome.app.window.create('app_main.html',
+ { 'width': 1920,
+ 'height': 1080 },
+ function(window) {
+ });
+});
diff --git a/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/manifest.json b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/manifest.json
new file mode 100644
index 0000000..f451d47
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/multi_app_kiosk/src/secondary_app_3/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2,
+ "name": "Kiosk secondary app 3",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "1.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/chrome/test/data/chromeos/app_mode/webstore/downloads/aabnpdpieclcikafhdkkpldcaodmfoai-1.0.0.crx b/chrome/test/data/chromeos/app_mode/webstore/downloads/aabnpdpieclcikafhdkkpldcaodmfoai-1.0.0.crx
new file mode 100644
index 0000000..d77cb21
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/webstore/downloads/aabnpdpieclcikafhdkkpldcaodmfoai-1.0.0.crx
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-1.0.0.crx b/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-1.0.0.crx
new file mode 100644
index 0000000..cb5c5e0
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-1.0.0.crx
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-2.0.0-1app.crx b/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-2.0.0-1app.crx
new file mode 100644
index 0000000..a7e8657
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-2.0.0-1app.crx
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-3.0.0-3app.crx b/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-3.0.0-3app.crx
new file mode 100644
index 0000000..8768f88
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/webstore/downloads/ceobkcclegcliomogfoeoheahogoecgl-3.0.0-3app.crx
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/webstore/downloads/fiehokkcgaojmbhfhlpiheggjhaedjoc-1.0.0.crx b/chrome/test/data/chromeos/app_mode/webstore/downloads/fiehokkcgaojmbhfhlpiheggjhaedjoc-1.0.0.crx
new file mode 100644
index 0000000..4b90489
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/webstore/downloads/fiehokkcgaojmbhfhlpiheggjhaedjoc-1.0.0.crx
Binary files differ
diff --git a/chrome/test/data/chromeos/app_mode/webstore/downloads/ihplaomghjbeafnpnjkhppmfpnmdihgd-1.0.0.crx b/chrome/test/data/chromeos/app_mode/webstore/downloads/ihplaomghjbeafnpnjkhppmfpnmdihgd-1.0.0.crx
new file mode 100644
index 0000000..734efcc
--- /dev/null
+++ b/chrome/test/data/chromeos/app_mode/webstore/downloads/ihplaomghjbeafnpnjkhppmfpnmdihgd-1.0.0.crx
Binary files differ
diff --git a/chromeos/chromeos_paths.cc b/chromeos/chromeos_paths.cc
index 3132c32..64005cc 100644
--- a/chromeos/chromeos_paths.cc
+++ b/chromeos/chromeos_paths.cc
@@ -47,6 +47,9 @@ const base::FilePath::CharType kDeviceLocalAccountExternalDataDir[] =
const base::FilePath::CharType kDeviceLocalAccountComponentPolicy[] =
FILE_PATH_LITERAL("/var/cache/device_local_account_component_policy");
+const base::FilePath::CharType kDeviceExtensionLocalCache[] =
+ FILE_PATH_LITERAL("/var/cache/external_cache");
+
const base::FilePath::CharType kDeviceColorProfileDirectory[] =
FILE_PATH_LITERAL("/usr/share/color/icc");
@@ -85,6 +88,9 @@ bool PathProvider(int key, base::FilePath* result) {
case DIR_DEVICE_COLOR_CALIBRATION_PROFILES:
*result = base::FilePath(kDeviceColorProfileDirectory);
break;
+ case DIR_DEVICE_EXTENSION_LOCAL_CACHE:
+ *result = base::FilePath(kDeviceExtensionLocalCache);
+ break;
default:
return false;
}
@@ -131,6 +137,9 @@ void RegisterStubPathOverrides(const base::FilePath& stubs_dir) {
PathService::Override(
DIR_DEVICE_LOCAL_ACCOUNT_COMPONENT_POLICY,
parent.AppendASCII("stub_device_local_account_component_policy"));
+ PathService::Override(
+ DIR_DEVICE_EXTENSION_LOCAL_CACHE,
+ parent.AppendASCII("stub_device_local_extension_cache"));
}
} // namespace chromeos
diff --git a/chromeos/chromeos_paths.h b/chromeos/chromeos_paths.h
index 2e500d9..95fdcfa 100644
--- a/chromeos/chromeos_paths.h
+++ b/chromeos/chromeos_paths.h
@@ -46,6 +46,8 @@ enum {
// policy for extensions.
DIR_DEVICE_COLOR_CALIBRATION_PROFILES, // Directory where system color
// calibration files can be found.
+ DIR_DEVICE_EXTENSION_LOCAL_CACHE, // Directory where extension local cache
+ // is stored.
PATH_END
};
diff --git a/extensions/common/api/_manifest_features.json b/extensions/common/api/_manifest_features.json
index 42b3288..d50cc4f 100644
--- a/extensions/common/api/_manifest_features.json
+++ b/extensions/common/api/_manifest_features.json
@@ -175,6 +175,12 @@
"platform_app"
]
},
+ "kiosk_secondary_apps": {
+ "channel": "stable",
+ "extension_types": [
+ "platform_app"
+ ]
+ },
"launcher_page": {
"channel": "stable",
"min_manifest_version": 2,
diff --git a/extensions/common/api/extensions_manifest_types.json b/extensions/common/api/extensions_manifest_types.json
index d698273..6057e4e 100644
--- a/extensions/common/api/extensions_manifest_types.json
+++ b/extensions/common/api/extensions_manifest_types.json
@@ -207,7 +207,21 @@
}
}
}
- }
+ },
+ {
+ "id": "KioskSecondaryApps",
+ "type": "array",
+ "description": "The <code>kiosk_secondary_apps</code> manifest property lists the secondary kiosk apps to be deployed by the primary kiosk app.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "ID of secondary kiosk app",
+ "type": "string"
+ }
+ }
+ }
+ }
]
}
]
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 26c9426..6dd9a41 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -68,6 +68,7 @@ const char kKeycode[] = "keyCode";
const char kKioskEnabled[] = "kiosk_enabled";
const char kKioskOnly[] = "kiosk_only";
const char kKioskMode[] = "kiosk_mode";
+const char kKioskSecondaryApps[] = "kiosk_secondary_apps";
const char kLanguage[] = "language";
const char kLaunch[] = "app.launch";
const char kLaunchContainer[] = "app.launch.container";
@@ -458,6 +459,12 @@ const char kInvalidKioskOnly[] =
"Invalid value for 'kiosk_only'.";
const char kInvalidKioskOnlyButNotEnabled[] =
"The 'kiosk_only' key is set, but 'kiosk_enabled' is not set.";
+const char kInvalidKioskSecondaryApps[] =
+ "Invalid value for 'kiosk_secondary_apps'";
+const char kInvalidKioskSecondaryAppsBadAppEntry[] =
+ "Invalid app id item for 'kiosk_secondary_apps'";
+const char kInvalidKioskSecondaryAppsBadAppId[] =
+ "Invalid app id value for 'kiosk_secondary_apps'";
const char kInvalidLauncherPage[] = "Invalid value for 'launcher_page'.";
const char kInvalidLauncherPagePage[] =
"Invalid value for 'launcher_page.page'.";
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index 607e34c..791d504 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -71,6 +71,7 @@ extern const char kKeycode[];
extern const char kKioskEnabled[];
extern const char kKioskOnly[];
extern const char kKioskMode[];
+extern const char kKioskSecondaryApps[];
extern const char kLanguage[];
extern const char kLaunch[];
extern const char kLaunchContainer[];
@@ -350,6 +351,9 @@ extern const char kInvalidKeyBindingUnknownPlatform[];
extern const char kInvalidKioskEnabled[];
extern const char kInvalidKioskOnly[];
extern const char kInvalidKioskOnlyButNotEnabled[];
+extern const char kInvalidKioskSecondaryApps[];
+extern const char kInvalidKioskSecondaryAppsBadAppEntry[];
+extern const char kInvalidKioskSecondaryAppsBadAppId[];
extern const char kInvalidLauncherPage[];
extern const char kInvalidLauncherPagePage[];
extern const char kInvalidLaunchContainer[];
diff --git a/extensions/common/manifest_handlers/kiosk_mode_info.cc b/extensions/common/manifest_handlers/kiosk_mode_info.cc
index f5d36af..d265c23 100644
--- a/extensions/common/manifest_handlers/kiosk_mode_info.cc
+++ b/extensions/common/manifest_handlers/kiosk_mode_info.cc
@@ -9,36 +9,50 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "extensions/common/api/extensions_manifest_types.h"
#include "extensions/common/manifest_constants.h"
namespace extensions {
namespace keys = manifest_keys;
-KioskModeInfo::KioskModeInfo(KioskStatus kiosk_status)
- : kiosk_status(kiosk_status) {
-}
+using api::extensions_manifest_types::KioskSecondaryAppsType;
+
+KioskModeInfo::KioskModeInfo(KioskStatus kiosk_status,
+ const std::vector<std::string>& secondary_app_ids)
+ : kiosk_status(kiosk_status), secondary_app_ids(secondary_app_ids) {}
KioskModeInfo::~KioskModeInfo() {
}
// static
-bool KioskModeInfo::IsKioskEnabled(const Extension* extension) {
- KioskModeInfo* info = static_cast<KioskModeInfo*>(
+KioskModeInfo* KioskModeInfo::Get(const Extension* extension) {
+ return static_cast<KioskModeInfo*>(
extension->GetManifestData(keys::kKioskMode));
+}
+
+// static
+bool KioskModeInfo::IsKioskEnabled(const Extension* extension) {
+ KioskModeInfo* info = Get(extension);
return info ? info->kiosk_status != NONE : false;
}
// static
bool KioskModeInfo::IsKioskOnly(const Extension* extension) {
- KioskModeInfo* info = static_cast<KioskModeInfo*>(
- extension->GetManifestData(keys::kKioskMode));
+ KioskModeInfo* info = Get(extension);
return info ? info->kiosk_status == ONLY : false;
}
+// static
+bool KioskModeInfo::HasSecondaryApps(const Extension* extension) {
+ KioskModeInfo* info = Get(extension);
+ return info && !info->secondary_app_ids.empty();
+}
+
KioskModeHandler::KioskModeHandler() {
supported_keys_.push_back(keys::kKioskEnabled);
supported_keys_.push_back(keys::kKioskOnly);
+ supported_keys_.push_back(keys::kKioskSecondaryApps);
}
KioskModeHandler::~KioskModeHandler() {
@@ -77,8 +91,32 @@ bool KioskModeHandler::Parse(Extension* extension, base::string16* error) {
if (kiosk_enabled)
kiosk_status = kiosk_only ? KioskModeInfo::ONLY : KioskModeInfo::ENABLED;
+ // Kiosk secondary apps key is optional.
+ std::vector<std::string> ids;
+ if (extension->manifest()->HasKey(keys::kKioskSecondaryApps)) {
+ const base::Value* secondary_apps = nullptr;
+ const base::ListValue* list = nullptr;
+ if (!extension->manifest()->Get(keys::kKioskSecondaryApps,
+ &secondary_apps) ||
+ !secondary_apps->GetAsList(&list)) {
+ *error = base::ASCIIToUTF16(manifest_errors::kInvalidKioskSecondaryApps);
+ return false;
+ }
+
+ for (const base::Value* value : *list) {
+ scoped_ptr<KioskSecondaryAppsType> app =
+ KioskSecondaryAppsType::FromValue(*value, error);
+ if (!app) {
+ *error = base::ASCIIToUTF16(
+ manifest_errors::kInvalidKioskSecondaryAppsBadAppId);
+ return false;
+ }
+ ids.push_back(app->id);
+ }
+ }
+
extension->SetManifestData(keys::kKioskMode,
- new KioskModeInfo(kiosk_status));
+ new KioskModeInfo(kiosk_status, ids));
return true;
}
diff --git a/extensions/common/manifest_handlers/kiosk_mode_info.h b/extensions/common/manifest_handlers/kiosk_mode_info.h
index cf02b0d..9de03a3 100644
--- a/extensions/common/manifest_handlers/kiosk_mode_info.h
+++ b/extensions/common/manifest_handlers/kiosk_mode_info.h
@@ -22,16 +22,27 @@ struct KioskModeInfo : public Extension::ManifestData {
ONLY
};
- explicit KioskModeInfo(KioskStatus kiosk_status);
+ KioskModeInfo(KioskStatus kiosk_status,
+ const std::vector<std::string>& secondary_app_ids);
~KioskModeInfo() override;
- KioskStatus kiosk_status;
+ // Gets the KioskModeInfo for |extension|, or NULL if none was
+ // specified.
+ static KioskModeInfo* Get(const Extension* extension);
// Whether the extension or app is enabled for app kiosk mode.
static bool IsKioskEnabled(const Extension* extension);
// Whether the extension or app should only be available in kiosk mode.
static bool IsKioskOnly(const Extension* extension);
+
+ // Returns true if |extension| declares kiosk secondary apps.
+ static bool HasSecondaryApps(const Extension* extension);
+
+ KioskStatus kiosk_status;
+
+ // The IDs of the kiosk secondary apps.
+ const std::vector<std::string> secondary_app_ids;
};
// Parses the "kiosk_enabled" and "kiosk_only" manifest keys.
diff --git a/extensions/common/manifest_handlers/kiosk_mode_info_unittest.cc b/extensions/common/manifest_handlers/kiosk_mode_info_unittest.cc
new file mode 100644
index 0000000..9e42c18
--- /dev/null
+++ b/extensions/common/manifest_handlers/kiosk_mode_info_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 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 "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/kiosk_mode_info.h"
+#include "extensions/common/manifest_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+using KioskModeInfoManifestTest = ManifestTest;
+
+TEST_F(KioskModeInfoManifestTest, NoSecondaryApps) {
+ scoped_refptr<Extension> extension(
+ LoadAndExpectSuccess("kiosk_secondary_app_no_secondary_app.json"));
+ EXPECT_FALSE(KioskModeInfo::HasSecondaryApps(extension.get()));
+}
+
+TEST_F(KioskModeInfoManifestTest, MultipleSecondaryApps) {
+ const std::string expected_ids[] = {
+ "fiehokkcgaojmbhfhlpiheggjhaedjoc",
+ "ihplaomghjbeafnpnjkhppmfpnmdihgd"};
+ scoped_refptr<Extension> extension(
+ LoadAndExpectSuccess("kiosk_secondary_app_multi_apps.json"));
+ EXPECT_TRUE(KioskModeInfo::HasSecondaryApps(extension.get()));
+ KioskModeInfo* info = KioskModeInfo::Get(extension.get());
+ EXPECT_NE(nullptr, info);
+ std::vector<std::string> parsed_ids(info->secondary_app_ids);
+ std::sort(parsed_ids.begin(), parsed_ids.end());
+ EXPECT_TRUE(
+ std::equal(parsed_ids.begin(), parsed_ids.end(), expected_ids));
+}
+
+} // namespace extensions
diff --git a/extensions/extensions_tests.gypi b/extensions/extensions_tests.gypi
index cd9f48c..e380f27 100644
--- a/extensions/extensions_tests.gypi
+++ b/extensions/extensions_tests.gypi
@@ -124,6 +124,7 @@
'common/manifest_handlers/default_locale_manifest_unittest.cc',
'common/manifest_handlers/externally_connectable_unittest.cc',
'common/manifest_handlers/file_handler_manifest_unittest.cc',
+ 'common/manifest_handlers/kiosk_mode_info_unittest.cc',
'common/manifest_handlers/oauth2_manifest_unittest.cc',
'common/manifest_handlers/shared_module_manifest_unittest.cc',
'common/message_bundle_unittest.cc',
diff --git a/extensions/test/data/manifest_tests/kiosk_secondary_app_multi_apps.json b/extensions/test/data/manifest_tests/kiosk_secondary_app_multi_apps.json
new file mode 100644
index 0000000..60e4b2c
--- /dev/null
+++ b/extensions/test/data/manifest_tests/kiosk_secondary_app_multi_apps.json
@@ -0,0 +1,24 @@
+{
+ "manifest_version": 2,
+ "name": "Test Kiosk App for multi-apps",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "1.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "kiosk_secondary_apps": [
+ {"id": "ihplaomghjbeafnpnjkhppmfpnmdihgd"},
+ {"id": "fiehokkcgaojmbhfhlpiheggjhaedjoc"}
+ ],
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/extensions/test/data/manifest_tests/kiosk_secondary_app_no_secondary_app.json b/extensions/test/data/manifest_tests/kiosk_secondary_app_no_secondary_app.json
new file mode 100644
index 0000000..27aeabe
--- /dev/null
+++ b/extensions/test/data/manifest_tests/kiosk_secondary_app_no_secondary_app.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2,
+ "name": "Kiosk app",
+ "minimum_chrome_version": "24.0.1307.0",
+ "version": "1.0.0",
+ "icons": {
+ "128": "icon-128.png",
+ "16": "icon-16.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["main.js"]
+ }
+ },
+ "kiosk_enabled": true,
+ "offline_enabled": true,
+ "permissions": [
+ "power"
+ ]
+}
diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py
index facd5e4..0035f7f 100644
--- a/tools/json_schema_compiler/h_generator.py
+++ b/tools/json_schema_compiler/h_generator.py
@@ -195,7 +195,7 @@ class _Generator(object):
elif type_.property_type == PropertyType.ARRAY:
if generate_typedefs and type_.description:
c.Comment(type_.description)
- c.Cblock(self._GenerateType(type_.item_type))
+ c.Cblock(self._GenerateType(type_.item_type, is_toplevel=is_toplevel))
if generate_typedefs:
(c.Append('typedef std::vector<%s > %s;' % (
self._type_helper.GetCppType(type_.item_type),