Commit 45eeb4a8 authored by lfg's avatar lfg Committed by Commit bot

Add the ability to load multiple apps into app_shell.

  * To load the apps you can specify a comma-separated list in the command-line.
    For example: 'app_shell --app-shell-app-path=path/to/app1/,path/to/app2'

  * For now there's no API to control the focus of the apps, so the only way
    to switch apps is to either close the app_window (which will switch focus to
    the next app in the hierarchy) or using the aura::Window API directly and
    calling Focus() on the windows.

BUG=422618

Review URL: https://codereview.chromium.org/642383003

Cr-Commit-Position: refs/heads/master@{#301135}
parent 1610bcc9
......@@ -6,6 +6,7 @@
#include "athena/extensions/shell/athena_shell_app_window_client.h"
#include "base/macros.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/install/extension_install_ui.h"
#include "extensions/common/extension_set.h"
#include "extensions/shell/browser/shell_extension_system.h"
......@@ -32,13 +33,10 @@ class ShellExtensionsDelegate : public ExtensionsDelegate {
return context_;
}
virtual const extensions::ExtensionSet& GetInstalledExtensions() override {
shell_extensions_.Clear();
if (extension_system_->extension().get())
shell_extensions_.Insert(extension_system_->extension());
return shell_extensions_;
return extensions::ExtensionRegistry::Get(context_)->enabled_extensions();
}
virtual bool LaunchApp(const std::string& app_id) override {
extension_system_->LaunchApp();
extension_system_->LaunchApp(app_id);
return true;
}
......@@ -51,7 +49,6 @@ class ShellExtensionsDelegate : public ExtensionsDelegate {
content::BrowserContext* context_;
extensions::ShellExtensionSystem* extension_system_;
extensions::ExtensionSet shell_extensions_;
AthenaShellAppWindowClient app_window_client_;
......
......@@ -69,6 +69,8 @@ class AthenaDesktopController : public extensions::DesktopController {
NOTIMPLEMENTED();
}
virtual void RemoveAppWindow(extensions::AppWindow* window) override {}
// Closes and destroys the app windows.
virtual void CloseAppWindows() override {}
......
......@@ -140,8 +140,9 @@ void WebViewAPITest::LaunchApp(const std::string& app_location) {
embedded_test_server()->ServeFilesFromDirectory(test_data_dir);
ASSERT_TRUE(extension_system_->LoadApp(test_data_dir));
extension_system_->LaunchApp();
const Extension* extension = extension_system_->LoadApp(test_data_dir);
ASSERT_TRUE(extension);
extension_system_->LaunchApp(extension->id());
ExtensionTestMessageListener launch_listener("LAUNCHED", false);
ASSERT_TRUE(launch_listener.WaitUntilSatisfied());
......
......@@ -7,6 +7,7 @@
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/string_tokenizer.h"
#include "extensions/shell/browser/shell_desktop_controller.h"
#include "extensions/shell/browser/shell_extension_system.h"
#include "extensions/shell/common/switches.h"
......@@ -23,15 +24,25 @@ void DefaultShellBrowserMainDelegate::Start(
content::BrowserContext* browser_context) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kAppShellAppPath)) {
base::FilePath app_dir(
command_line->GetSwitchValueNative(switches::kAppShellAppPath));
base::FilePath app_absolute_dir = base::MakeAbsoluteFilePath(app_dir);
ShellExtensionSystem* extension_system = static_cast<ShellExtensionSystem*>(
ExtensionSystem::Get(browser_context));
if (!extension_system->LoadApp(app_absolute_dir))
return;
extension_system->LaunchApp();
extension_system->Init();
CommandLine::StringType path_list =
command_line->GetSwitchValueNative(switches::kAppShellAppPath);
base::StringTokenizerT<CommandLine::StringType,
CommandLine::StringType::const_iterator>
tokenizer(path_list, FILE_PATH_LITERAL(","));
while (tokenizer.GetNext()) {
base::FilePath app_absolute_dir =
base::MakeAbsoluteFilePath(base::FilePath(tokenizer.token()));
const Extension* extension = extension_system->LoadApp(app_absolute_dir);
if (!extension)
continue;
extension_system->LaunchApp(extension->id());
}
} else {
LOG(ERROR) << "--" << switches::kAppShellAppPath
<< " unset; boredom is in your future";
......
......@@ -46,6 +46,9 @@ class DesktopController {
// Attaches the window to our window hierarchy.
virtual void AddAppWindow(aura::Window* window) = 0;
// Removes the window from the desktop.
virtual void RemoveAppWindow(AppWindow* window) = 0;
// Closes and destroys the app windows.
virtual void CloseAppWindows() = 0;
};
......
......@@ -4,6 +4,7 @@
#include "extensions/shell/browser/shell_desktop_controller.h"
#include <algorithm>
#include <string>
#include <vector>
......@@ -160,7 +161,7 @@ class AppsFocusRules : public wm::BaseFocusRules {
} // namespace
ShellDesktopController::ShellDesktopController()
: app_window_client_(new ShellAppWindowClient), app_window_(NULL) {
: app_window_client_(new ShellAppWindowClient) {
extensions::AppWindowClient::Set(app_window_client_.get());
#if defined(OS_CHROMEOS)
......@@ -191,8 +192,9 @@ aura::WindowTreeHost* ShellDesktopController::GetHost() {
AppWindow* ShellDesktopController::CreateAppWindow(
content::BrowserContext* context,
const Extension* extension) {
app_window_ = new AppWindow(context, new ShellAppDelegate, extension);
return app_window_;
app_windows_.push_back(
new AppWindow(context, new ShellAppDelegate, extension));
return app_windows_.back();
}
void ShellDesktopController::AddAppWindow(aura::Window* window) {
......@@ -200,11 +202,20 @@ void ShellDesktopController::AddAppWindow(aura::Window* window) {
root_window->AddChild(window);
}
void ShellDesktopController::RemoveAppWindow(AppWindow* window) {
auto iter = std::find(app_windows_.begin(), app_windows_.end(), window);
DCHECK(iter != app_windows_.end());
app_windows_.erase(iter);
}
void ShellDesktopController::CloseAppWindows() {
if (app_window_) {
app_window_->GetBaseWindow()->Close(); // Close() deletes |app_window_|.
app_window_ = NULL;
}
// Create a copy of the window vector, because closing the windows will
// trigger RemoveAppWindow, which will invalidate the iterator.
// This vector should be small enough that this should not be an issue.
std::vector<AppWindow*> app_windows(app_windows_);
for (AppWindow* app_window : app_windows)
app_window->GetBaseWindow()->Close(); // Close() deletes |app_window|.
app_windows_.clear();
}
aura::Window* ShellDesktopController::GetDefaultParent(
......
......@@ -5,6 +5,8 @@
#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_DESKTOP_CONTROLLER_H_
#define EXTENSIONS_SHELL_BROWSER_SHELL_DESKTOP_CONTROLLER_H_
#include <vector>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
......@@ -69,6 +71,7 @@ class ShellDesktopController : public DesktopController,
virtual AppWindow* CreateAppWindow(content::BrowserContext* context,
const Extension* extension) override;
virtual void AddAppWindow(aura::Window* window) override;
virtual void RemoveAppWindow(AppWindow* window) override;
virtual void CloseAppWindows() override;
// aura::client::WindowTreeClient overrides:
......@@ -131,8 +134,8 @@ class ShellDesktopController : public DesktopController,
scoped_ptr<AppWindowClient> app_window_client_;
// The desktop supports a single app window.
AppWindow* app_window_; // NativeAppWindow::Close() deletes this.
// NativeAppWindow::Close() deletes the AppWindow.
std::vector<AppWindow*> app_windows_;
DISALLOW_COPY_AND_ASSIGN(ShellDesktopController);
};
......
......@@ -37,21 +37,20 @@ ShellExtensionSystem::ShellExtensionSystem(BrowserContext* browser_context)
ShellExtensionSystem::~ShellExtensionSystem() {
}
bool ShellExtensionSystem::LoadApp(const base::FilePath& app_dir) {
const Extension* ShellExtensionSystem::LoadApp(const base::FilePath& app_dir) {
// app_shell only supports unpacked extensions.
// NOTE: If you add packed extension support consider removing the flag
// FOLLOW_SYMLINKS_ANYWHERE below. Packed extensions should not have symlinks.
CHECK(base::DirectoryExists(app_dir)) << app_dir.AsUTF8Unsafe();
int load_flags = Extension::FOLLOW_SYMLINKS_ANYWHERE;
std::string load_error;
extension_ = file_util::LoadExtension(
scoped_refptr<Extension> extension = file_util::LoadExtension(
app_dir, Manifest::COMMAND_LINE, load_flags, &load_error);
if (!extension_.get()) {
if (!extension.get()) {
LOG(ERROR) << "Loading extension at " << app_dir.value()
<< " failed with: " << load_error;
return false;
return nullptr;
}
app_id_ = extension_->id();
// TODO(jamescook): We may want to do some of these things here:
// * Create a PermissionsUpdater.
......@@ -60,29 +59,36 @@ bool ShellExtensionSystem::LoadApp(const base::FilePath& app_dir) {
// * Call ExtensionPrefs::OnExtensionInstalled().
// * Send NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED.
ExtensionRegistry::Get(browser_context_)->AddEnabled(extension_);
ExtensionRegistry::Get(browser_context_)->AddEnabled(extension.get());
RegisterExtensionWithRequestContexts(extension_.get());
RegisterExtensionWithRequestContexts(extension.get());
content::NotificationService::current()->Notify(
extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
content::Source<BrowserContext>(browser_context_),
content::Details<const Extension>(extension_.get()));
content::Details<const Extension>(extension.get()));
return extension.get();
}
void ShellExtensionSystem::Init() {
// Inform the rest of the extensions system to start.
ready_.Signal();
content::NotificationService::current()->Notify(
extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
content::Source<BrowserContext>(browser_context_),
content::NotificationService::NoDetails());
return true;
}
void ShellExtensionSystem::LaunchApp() {
void ShellExtensionSystem::LaunchApp(const ExtensionId& extension_id) {
// Send the onLaunched event.
DCHECK(extension_.get());
AppRuntimeEventRouter::DispatchOnLaunchedEvent(browser_context_,
extension_.get());
DCHECK(ExtensionRegistry::Get(browser_context_)
->enabled_extensions()
.Contains(extension_id));
const Extension* extension = ExtensionRegistry::Get(browser_context_)
->enabled_extensions()
.GetByID(extension_id);
AppRuntimeEventRouter::DispatchOnLaunchedEvent(browser_context_, extension);
}
void ShellExtensionSystem::Shutdown() {
......
......@@ -38,17 +38,19 @@ class ShellExtensionSystem : public ExtensionSystem {
explicit ShellExtensionSystem(content::BrowserContext* browser_context);
~ShellExtensionSystem() override;
// Loads an unpacked application from a directory. Returns true on success.
bool LoadApp(const base::FilePath& app_dir);
// Loads an unpacked application from a directory. Returns the extension on
// success, or null otherwise.
const Extension* LoadApp(const base::FilePath& app_dir);
// Launch the currently loaded app.
void LaunchApp();
// Initializes the extension system.
void Init();
// Launch the app with id |extension_id|.
void LaunchApp(const std::string& extension_id);
// KeyedService implementation:
void Shutdown() override;
scoped_refptr<Extension> extension() { return extension_; }
// ExtensionSystem implementation:
void InitForRegularProfile(bool extensions_enabled) override;
ExtensionService* extension_service() override;
......@@ -81,11 +83,6 @@ class ShellExtensionSystem : public ExtensionSystem {
private:
content::BrowserContext* browser_context_; // Not owned.
// Extension ID for the app.
std::string app_id_;
scoped_refptr<Extension> extension_;
// Data to be accessed on the IO thread. Must outlive process_manager_.
scoped_refptr<InfoMap> info_map_;
......
......@@ -92,6 +92,7 @@ void ShellNativeAppWindow::ShowInactive() {
}
void ShellNativeAppWindow::Close() {
DesktopController::instance()->RemoveAppWindow(app_window_);
app_window_->OnNativeClose();
}
......
......@@ -24,11 +24,11 @@ bool ShellApiTest::RunAppTest(const std::string& app_dir) {
test_data_dir = test_data_dir.AppendASCII(app_dir);
ResultCatcher catcher;
bool loaded = extension_system_->LoadApp(test_data_dir);
if (!loaded)
const Extension* extension = extension_system_->LoadApp(test_data_dir);
if (!extension)
return false;
extension_system_->LaunchApp();
extension_system_->LaunchApp(extension->id());
if (!catcher.GetNextResult()) {
message_ = catcher.message();
......
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