Commit 93407a3e authored by Wenzhao Zang's avatar Wenzhao Zang Committed by Commit Bot

cros: Add onInstalled event to arcAppsPrivate API

The "installed" event is fired when
ArcAppListPrefs::Observer::OnAppRegistered is called, which is the time
that |launchApp| can be used to actually launch the app.

Bug: 819404
Change-Id: If874607a351fb2e0e24637c7b6b4c1013b6812ae
Reviewed-on: https://chromium-review.googlesource.com/1152703
Commit-Queue: Wenzhao (Colin) Zang <wzang@chromium.org>
Reviewed-by: default avatarToni Barzic <tbarzic@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581370}
parent bf776308
...@@ -7,12 +7,64 @@ ...@@ -7,12 +7,64 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/logging.h"
#include "base/no_destructor.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
#include "chrome/common/extensions/api/arc_apps_private.h" #include "chrome/common/extensions/api/arc_apps_private.h"
#include "ui/events/event_constants.h" #include "ui/events/event_constants.h"
namespace extensions {
// static
BrowserContextKeyedAPIFactory<ArcAppsPrivateAPI>*
ArcAppsPrivateAPI::GetFactoryInstance() {
static base::NoDestructor<BrowserContextKeyedAPIFactory<ArcAppsPrivateAPI>>
instance;
return instance.get();
}
ArcAppsPrivateAPI::ArcAppsPrivateAPI(content::BrowserContext* context)
: context_(context), scoped_prefs_observer_(this) {
EventRouter::Get(context_)->RegisterObserver(
this, api::arc_apps_private::OnInstalled::kEventName);
}
ArcAppsPrivateAPI::~ArcAppsPrivateAPI() = default;
void ArcAppsPrivateAPI::Shutdown() {
EventRouter::Get(context_)->UnregisterObserver(this);
scoped_prefs_observer_.RemoveAll();
}
void ArcAppsPrivateAPI::OnListenerAdded(const EventListenerInfo& details) {
DCHECK_EQ(details.event_name, api::arc_apps_private::OnInstalled::kEventName);
auto* prefs = ArcAppListPrefs::Get(Profile::FromBrowserContext(context_));
if (prefs && !scoped_prefs_observer_.IsObserving(prefs))
scoped_prefs_observer_.Add(prefs);
}
void ArcAppsPrivateAPI::OnListenerRemoved(const EventListenerInfo& details) {
if (!EventRouter::Get(context_)->HasEventListener(
api::arc_apps_private::OnInstalled::kEventName)) {
scoped_prefs_observer_.RemoveAll();
}
}
void ArcAppsPrivateAPI::OnAppRegistered(
const std::string& app_id,
const ArcAppListPrefs::AppInfo& app_info) {
if (!app_info.launchable)
return;
api::arc_apps_private::AppInfo app_info_result;
app_info_result.id = app_id;
auto event = std::make_unique<Event>(
events::ARC_APPS_PRIVATE_ON_INSTALLED,
api::arc_apps_private::OnInstalled::kEventName,
api::arc_apps_private::OnInstalled::Create(app_info_result), context_);
EventRouter::Get(context_)->BroadcastEvent(std::move(event));
}
ArcAppsPrivateGetLaunchableAppsFunction:: ArcAppsPrivateGetLaunchableAppsFunction::
ArcAppsPrivateGetLaunchableAppsFunction() = default; ArcAppsPrivateGetLaunchableAppsFunction() = default;
...@@ -21,24 +73,23 @@ ArcAppsPrivateGetLaunchableAppsFunction:: ...@@ -21,24 +73,23 @@ ArcAppsPrivateGetLaunchableAppsFunction::
ExtensionFunction::ResponseAction ExtensionFunction::ResponseAction
ArcAppsPrivateGetLaunchableAppsFunction::Run() { ArcAppsPrivateGetLaunchableAppsFunction::Run() {
ArcAppListPrefs* prefs = auto* prefs =
ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context())); ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context()));
if (!prefs) if (!prefs)
return RespondNow(Error("Not available")); return RespondNow(Error("Not available"));
std::vector<extensions::api::arc_apps_private::AppInfo> result; std::vector<api::arc_apps_private::AppInfo> result;
const std::vector<std::string> app_ids = prefs->GetAppIds(); const std::vector<std::string> app_ids = prefs->GetAppIds();
for (const auto& app_id : app_ids) { for (const auto& app_id : app_ids) {
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
if (app_info && app_info->launchable) { if (app_info && app_info->launchable) {
extensions::api::arc_apps_private::AppInfo app_info_result; api::arc_apps_private::AppInfo app_info_result;
app_info_result.id = app_id; app_info_result.id = app_id;
result.push_back(std::move(app_info_result)); result.push_back(std::move(app_info_result));
} }
} }
return RespondNow(ArgumentList( return RespondNow(ArgumentList(
extensions::api::arc_apps_private::GetLaunchableApps::Results::Create( api::arc_apps_private::GetLaunchableApps::Results::Create(result)));
result)));
} }
ArcAppsPrivateLaunchAppFunction::ArcAppsPrivateLaunchAppFunction() = default; ArcAppsPrivateLaunchAppFunction::ArcAppsPrivateLaunchAppFunction() = default;
...@@ -46,8 +97,8 @@ ArcAppsPrivateLaunchAppFunction::ArcAppsPrivateLaunchAppFunction() = default; ...@@ -46,8 +97,8 @@ ArcAppsPrivateLaunchAppFunction::ArcAppsPrivateLaunchAppFunction() = default;
ArcAppsPrivateLaunchAppFunction::~ArcAppsPrivateLaunchAppFunction() = default; ArcAppsPrivateLaunchAppFunction::~ArcAppsPrivateLaunchAppFunction() = default;
ExtensionFunction::ResponseAction ArcAppsPrivateLaunchAppFunction::Run() { ExtensionFunction::ResponseAction ArcAppsPrivateLaunchAppFunction::Run() {
std::unique_ptr<extensions::api::arc_apps_private::LaunchApp::Params> params( std::unique_ptr<api::arc_apps_private::LaunchApp::Params> params(
extensions::api::arc_apps_private::LaunchApp::Params::Create(*args_)); api::arc_apps_private::LaunchApp::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get()); EXTENSION_FUNCTION_VALIDATE(params.get());
if (!ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context()))) if (!ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context())))
return RespondNow(Error("Not available")); return RespondNow(Error("Not available"));
...@@ -58,4 +109,6 @@ ExtensionFunction::ResponseAction ArcAppsPrivateLaunchAppFunction::Run() { ...@@ -58,4 +109,6 @@ ExtensionFunction::ResponseAction ArcAppsPrivateLaunchAppFunction::Run() {
return RespondNow(Error("Launch failed")); return RespondNow(Error("Launch failed"));
} }
return RespondNow(NoArguments()); return RespondNow(NoArguments());
} }
\ No newline at end of file
} // namespace extensions
...@@ -6,8 +6,60 @@ ...@@ -6,8 +6,60 @@
#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_ARC_APPS_PRIVATE_API_H_ #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_ARC_APPS_PRIVATE_API_H_
#include "base/macros.h" #include "base/macros.h"
#include "base/scoped_observer.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function.h"
namespace extensions {
class ArcAppsPrivateAPI : public BrowserContextKeyedAPI,
public EventRouter::Observer,
public ArcAppListPrefs::Observer {
public:
static BrowserContextKeyedAPIFactory<ArcAppsPrivateAPI>* GetFactoryInstance();
explicit ArcAppsPrivateAPI(content::BrowserContext* context);
~ArcAppsPrivateAPI() override;
// BrowserContextKeyedAPI:
void Shutdown() override;
// EventRouter::Observer:
void OnListenerAdded(const EventListenerInfo& details) override;
void OnListenerRemoved(const EventListenerInfo& details) override;
// ArcAppListPrefs::Observer:
void OnAppRegistered(const std::string& app_id,
const ArcAppListPrefs::AppInfo& app_info) override;
private:
friend class BrowserContextKeyedAPIFactory<ArcAppsPrivateAPI>;
static const char* service_name() { return "ArcAppsPrivateAPI"; }
// BrowserContextKeyedAPI:
static const bool kServiceIsNULLWhileTesting = true;
content::BrowserContext* const context_;
ScopedObserver<ArcAppListPrefs, ArcAppsPrivateAPI> scoped_prefs_observer_;
DISALLOW_COPY_AND_ASSIGN(ArcAppsPrivateAPI);
};
template <>
struct BrowserContextFactoryDependencies<ArcAppsPrivateAPI> {
static void DeclareFactoryDependencies(
BrowserContextKeyedAPIFactory<ArcAppsPrivateAPI>* factory) {
factory->DependsOn(
ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
factory->DependsOn(ArcAppListPrefsFactory::GetInstance());
}
};
class ArcAppsPrivateGetLaunchableAppsFunction class ArcAppsPrivateGetLaunchableAppsFunction
: public UIThreadExtensionFunction { : public UIThreadExtensionFunction {
public: public:
...@@ -43,4 +95,6 @@ class ArcAppsPrivateLaunchAppFunction : public UIThreadExtensionFunction { ...@@ -43,4 +95,6 @@ class ArcAppsPrivateLaunchAppFunction : public UIThreadExtensionFunction {
DISALLOW_COPY_AND_ASSIGN(ArcAppsPrivateLaunchAppFunction); DISALLOW_COPY_AND_ASSIGN(ArcAppsPrivateLaunchAppFunction);
}; };
} // namespace extensions
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_ARC_APPS_PRIVATE_API_H_ #endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_ARC_APPS_PRIVATE_API_H_
\ No newline at end of file
...@@ -5,9 +5,11 @@ ...@@ -5,9 +5,11 @@
#include <memory> #include <memory>
#include "base/macros.h" #include "base/macros.h"
#include "base/path_service.h"
#include "chrome/browser/chromeos/arc/arc_util.h" #include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_session_manager_client.h" #include "chromeos/dbus/fake_session_manager_client.h"
#include "components/arc/arc_bridge_service.h" #include "components/arc/arc_bridge_service.h"
...@@ -15,6 +17,8 @@ ...@@ -15,6 +17,8 @@
#include "components/arc/arc_util.h" #include "components/arc/arc_util.h"
#include "components/arc/test/connection_holder_util.h" #include "components/arc/test/connection_holder_util.h"
#include "components/arc/test/fake_app_instance.h" #include "components/arc/test/fake_app_instance.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
namespace { namespace {
...@@ -72,17 +76,18 @@ IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, GetAppIdAndLaunchApp) { ...@@ -72,17 +76,18 @@ IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, GetAppIdAndLaunchApp) {
static_cast<arc::mojom::AppHost*>(prefs)->OnTaskCreated( static_cast<arc::mojom::AppHost*>(prefs)->OnTaskCreated(
0 /* task_id */, "Package_1", "Dummy_activity_1", "App_1", 0 /* task_id */, "Package_1", "Dummy_activity_1", "App_1",
base::nullopt /* intent */); base::nullopt /* intent */);
// Stopping the service makes the app non-ready. // Stopping the service makes the app non-ready.
arc::ArcServiceManager::Get()->arc_bridge_service()->app()->CloseInstance( arc::ArcServiceManager::Get()->arc_bridge_service()->app()->CloseInstance(
app_instance.get()); app_instance.get());
EXPECT_EQ(0u, app_instance->launch_requests().size()); EXPECT_EQ(0u, app_instance->launch_requests().size());
// Verify |chrome.arcAppsPrivate.getLaunchableApps| returns the id of // Verify |chrome.arcAppsPrivate.getLaunchableApps| returns the id of
// the launchable app only. The JS test will attempt to launch the app. // the launchable app only. The JS test will attempt to launch the app.
EXPECT_TRUE(RunPlatformAppTestWithArg( EXPECT_TRUE(RunPlatformAppTestWithArg(
"arc_app_launcher", "arc_app_launcher/launch_app",
ArcAppListPrefs::GetAppId("Package_0", "Dummy_activity_0").c_str())) ArcAppListPrefs::GetAppId("Package_0", "Dummy_activity_0").c_str()))
<< message_; << message_;
// Verify the app is not launched because it's not ready. // Verify the app is not launched because it's not ready.
EXPECT_EQ(0u, app_instance->launch_requests().size()); EXPECT_EQ(0u, app_instance->launch_requests().size());
// Restarting the service makes the app ready. Verify the app is launched // Restarting the service makes the app ready. Verify the app is launched
...@@ -91,4 +96,35 @@ IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, GetAppIdAndLaunchApp) { ...@@ -91,4 +96,35 @@ IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, GetAppIdAndLaunchApp) {
app_instance->SendRefreshAppList({launchable_app}); app_instance->SendRefreshAppList({launchable_app});
ASSERT_EQ(1u, app_instance->launch_requests().size()); ASSERT_EQ(1u, app_instance->launch_requests().size());
EXPECT_TRUE(app_instance->launch_requests()[0]->IsForApp(launchable_app)); EXPECT_TRUE(app_instance->launch_requests()[0]->IsForApp(launchable_app));
}
IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, OnInstalled) {
ArcAppListPrefs* prefs = ArcAppListPrefs::Get(browser()->profile());
ASSERT_TRUE(prefs);
std::unique_ptr<arc::FakeAppInstance> app_instance = CreateAppInstance(prefs);
arc::mojom::AppInfo launchable_app("App_0", "Package_0", "Dummy_activity_0");
// The JS test will observe the onInstalled event and attempt to launch the
// newly installed app.
SetCustomArg(ArcAppListPrefs::GetAppId("Package_0", "Dummy_activity_0"));
extensions::ResultCatcher catcher;
ExtensionTestMessageListener ready_listener("ready", false);
base::FilePath path =
test_data_dir_.AppendASCII("arc_app_launcher/install_event");
const extensions::Extension* app = LoadExtension(path);
ASSERT_TRUE(app);
EXPECT_TRUE(ready_listener.WaitUntilSatisfied());
EXPECT_EQ(0u, app_instance->launch_requests().size());
// Add one launchable app and one non-launchable app.
app_instance->SendRefreshAppList({launchable_app});
static_cast<arc::mojom::AppHost*>(prefs)->OnTaskCreated(
0 /* task_id */, "Package_1", "Dummy_activity_1", "App_1",
base::nullopt /* intent */);
// Verify the JS test receives the onInstalled event for the launchable app
// only, and the app is launched successfully.
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
ASSERT_EQ(1u, app_instance->launch_requests().size());
EXPECT_TRUE(app_instance->launch_requests()[0]->IsForApp(launchable_app));
} }
\ No newline at end of file
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include "ppapi/buildflags/buildflags.h" #include "ppapi/buildflags/buildflags.h"
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/extensions/arc_apps_private_api.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h" #include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/chromeos/extensions/input_method_api.h" #include "chrome/browser/chromeos/extensions/input_method_api.h"
#include "chrome/browser/chromeos/extensions/media_player_api.h" #include "chrome/browser/chromeos/extensions/media_player_api.h"
...@@ -74,6 +75,9 @@ namespace chrome_extensions { ...@@ -74,6 +75,9 @@ namespace chrome_extensions {
void EnsureBrowserContextKeyedServiceFactoriesBuilt() { void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
extensions::ActivityLog::GetFactoryInstance(); extensions::ActivityLog::GetFactoryInstance();
extensions::ActivityLogAPI::GetFactoryInstance(); extensions::ActivityLogAPI::GetFactoryInstance();
#if defined(OS_CHROMEOS)
extensions::ArcAppsPrivateAPI::GetFactoryInstance();
#endif
extensions::AutofillPrivateEventRouterFactory::GetInstance(); extensions::AutofillPrivateEventRouterFactory::GetInstance();
extensions::BluetoothLowEnergyAPI::GetFactoryInstance(); extensions::BluetoothLowEnergyAPI::GetFactoryInstance();
extensions::BookmarksAPI::GetFactoryInstance(); extensions::BookmarksAPI::GetFactoryInstance();
......
...@@ -27,4 +27,9 @@ namespace arcAppsPrivate { ...@@ -27,4 +27,9 @@ namespace arcAppsPrivate {
// called as soon as the launch is scheduled. // called as soon as the launch is scheduled.
static void launchApp(DOMString app_id, optional VoidCallback callback); static void launchApp(DOMString app_id, optional VoidCallback callback);
}; };
interface Events {
// Fires when a new app can be launched via $(ref:launchApp).
static void onInstalled(AppInfo app_info);
};
}; };
\ No newline at end of file
{
"key": "MIGqAgEAAiEAuTqZKXReYjq0LqmxcPXj5BhO4l43YRxiPm1A+Y6IqG8CAwEAAQIhAIwbSucUYqAij1AMg29STwFbL08USDm7SxcNgsW7aJzhAhEA6cjXcL5MFxl2geM2ZowcTQIRAMrUkXe2RyL2wuBDnV0VRasCEAkABWM+wDknbsuDTsXV4QUCEA5o66zFlmg1f8LPCu8CA+ECEC2UbOIhswH8ZhEictgqd5k=",
"name": "Install event test app",
"version": "1",
"manifest_version": 2,
"permissions": ["arcAppsPrivate"],
"app": {
"background": {
"scripts": ["test.js"]
}
}
}
\ No newline at end of file
// Copyright 2018 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 EXPECTED_APP_ID = null;
chrome.test.runTests([
function getConfig() {
chrome.test.getConfig(chrome.test.callbackPass(config => {
EXPECTED_APP_ID = config.customArg;
}));
},
function onInstalled() {
chrome.test.assertTrue(!!EXPECTED_APP_ID);
chrome.test.listenOnce(chrome.arcAppsPrivate.onInstalled, appInfo => {
chrome.test.assertEq({id: EXPECTED_APP_ID}, appInfo);
chrome.arcAppsPrivate.launchApp(appInfo.id, chrome.test.callbackPass());
});
chrome.test.sendMessage('ready');
}
]);
\ No newline at end of file
...@@ -24,4 +24,4 @@ chrome.app.runtime.onLaunched.addListener(function() { ...@@ -24,4 +24,4 @@ chrome.app.runtime.onLaunched.addListener(function() {
})); }));
} }
]); ]);
}); });
\ No newline at end of file
...@@ -440,6 +440,7 @@ enum HistogramValue { ...@@ -440,6 +440,7 @@ enum HistogramValue {
INPUT_METHOD_PRIVATE_ON_FOCUS, INPUT_METHOD_PRIVATE_ON_FOCUS,
SYSTEM_POWER_SOURCE_ONPOWERCHANGED, SYSTEM_POWER_SOURCE_ONPOWERCHANGED,
WEB_REQUEST_ON_ACTION_IGNORED, WEB_REQUEST_ON_ACTION_IGNORED,
ARC_APPS_PRIVATE_ON_INSTALLED,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -14910,6 +14910,7 @@ Called by update_net_error_codes.py.--> ...@@ -14910,6 +14910,7 @@ Called by update_net_error_codes.py.-->
<int value="422" label="INPUT_METHOD_PRIVATE_ON_FOCUS"/> <int value="422" label="INPUT_METHOD_PRIVATE_ON_FOCUS"/>
<int value="423" label="SYSTEM_POWER_SOURCE_ONPOWERCHANGED"/> <int value="423" label="SYSTEM_POWER_SOURCE_ONPOWERCHANGED"/>
<int value="424" label="WEB_REQUEST_ON_ACTION_IGNORED"/> <int value="424" label="WEB_REQUEST_ON_ACTION_IGNORED"/>
<int value="425" label="ARC_APPS_PRIVATE_ON_INSTALLED"/>
</enum> </enum>
<enum name="ExtensionFileWriteResult"> <enum name="ExtensionFileWriteResult">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment