910e62b5创建于 1月15日历史提交
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/api/app_runtime/app_runtime_api.h"

#include <stddef.h>

#include <memory>
#include <utility>
#include <vector>

#include "base/time/time.h"
#include "base/types/cxx23_to_underlying.h"
#include "extensions/browser/entry_info.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/granted_file_entry.h"
#include "extensions/common/api/app_runtime.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/feature_switch.h"
#include "url/gurl.h"

using content::BrowserContext;

namespace extensions {

namespace app_runtime = api::app_runtime;

namespace {

void DispatchOnEmbedRequestedEventImpl(
    const ExtensionId& extension_id,
    base::Value::Dict app_embedding_request_data,
    content::BrowserContext* context) {
  base::Value::List args;
  args.Append(std::move(app_embedding_request_data));
  auto event = std::make_unique<Event>(
      events::APP_RUNTIME_ON_EMBED_REQUESTED,
      app_runtime::OnEmbedRequested::kEventName, std::move(args), context);
  EventRouter::Get(context)->DispatchEventWithLazyListener(extension_id,
                                                           std::move(event));

  ExtensionPrefs::Get(context)->SetLastLaunchTime(extension_id,
                                                  base::Time::Now());
}

void DispatchOnLaunchedEventImpl(const ExtensionId& extension_id,
                                 app_runtime::LaunchSource source,
                                 base::Value::Dict launch_data,
                                 BrowserContext* context) {
  launch_data.Set("isDemoSession",
                  ExtensionsBrowserClient::Get()->IsInDemoMode());

  // "Forced app mode" is true for Chrome OS kiosk mode.
  launch_data.Set("isKioskSession",
                  ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode());

  launch_data.Set("isPublicSession",
                  ExtensionsBrowserClient::Get()->IsLoggedInAsPublicAccount());

  base::Value::List args;
  args.Append(std::move(launch_data));
  auto event = std::make_unique<Event>(events::APP_RUNTIME_ON_LAUNCHED,
                                       app_runtime::OnLaunched::kEventName,
                                       std::move(args), context);
  EventRouter::Get(context)->DispatchEventWithLazyListener(extension_id,
                                                           std::move(event));
  ExtensionPrefs::Get(context)->SetLastLaunchTime(extension_id,
                                                  base::Time::Now());
}

#define ASSERT_ENUM_EQUAL(Name, Name2)                                     \
  static_assert(base::to_underlying(extensions::AppLaunchSource::Name) ==  \
                    base::to_underlying(app_runtime::LaunchSource::Name2), \
                "The value of extensions::" #Name                          \
                " and app_runtime::LAUNCH_" #Name2 " should be the same");

app_runtime::LaunchSource GetLaunchSourceEnum(AppLaunchSource source) {
  ASSERT_ENUM_EQUAL(kSourceNone, kNone);
  ASSERT_ENUM_EQUAL(kSourceUntracked, kUntracked);
  ASSERT_ENUM_EQUAL(kSourceAppLauncher, kAppLauncher);
  ASSERT_ENUM_EQUAL(kSourceNewTabPage, kNewTabPage);
  ASSERT_ENUM_EQUAL(kSourceReload, kReload);
  ASSERT_ENUM_EQUAL(kSourceRestart, kRestart);
  ASSERT_ENUM_EQUAL(kSourceLoadAndLaunch, kLoadAndLaunch);
  ASSERT_ENUM_EQUAL(kSourceCommandLine, kCommandLine);
  ASSERT_ENUM_EQUAL(kSourceFileHandler, kFileHandler);
  ASSERT_ENUM_EQUAL(kSourceUrlHandler, kUrlHandler);
  ASSERT_ENUM_EQUAL(kSourceSystemTray, kSystemTray);
  ASSERT_ENUM_EQUAL(kSourceAboutPage, kAboutPage);
  ASSERT_ENUM_EQUAL(kSourceKeyboard, kKeyboard);
  ASSERT_ENUM_EQUAL(kSourceExtensionsPage, kExtensionsPage);
  ASSERT_ENUM_EQUAL(kSourceManagementApi, kManagementApi);
  ASSERT_ENUM_EQUAL(kSourceEphemeralAppDeprecated, kEphemeralApp);
  ASSERT_ENUM_EQUAL(kSourceBackground, kBackground);
  ASSERT_ENUM_EQUAL(kSourceKiosk, kKiosk);
  ASSERT_ENUM_EQUAL(kSourceChromeInternal, kChromeInternal);
  ASSERT_ENUM_EQUAL(kSourceTest, kTest);
  ASSERT_ENUM_EQUAL(kSourceInstalledNotification, kInstalledNotification);
  ASSERT_ENUM_EQUAL(kSourceContextMenu, kContextMenu);
  ASSERT_ENUM_EQUAL(kSourceArc, kArc);
  ASSERT_ENUM_EQUAL(kSourceIntentUrl, kIntentUrl);

  // The +3 accounts for kSourceRunOnOsLogin, kSourceProtocolHandler and
  // kSourceReparenting not having a corresponding entry in
  // app_runtime::LaunchSource.
  static_assert(
      base::to_underlying(extensions::AppLaunchSource::kMaxValue) ==
          base::to_underlying(app_runtime::LaunchSource::kMaxValue) + 3,
      "");

  switch (source) {
    case AppLaunchSource::kSourceNone:
    case AppLaunchSource::kSourceUntracked:
    case AppLaunchSource::kSourceAppLauncher:
    case AppLaunchSource::kSourceNewTabPage:
    case AppLaunchSource::kSourceReload:
    case AppLaunchSource::kSourceRestart:
    case AppLaunchSource::kSourceLoadAndLaunch:
    case AppLaunchSource::kSourceCommandLine:
    case AppLaunchSource::kSourceFileHandler:
    case AppLaunchSource::kSourceUrlHandler:
    case AppLaunchSource::kSourceSystemTray:
    case AppLaunchSource::kSourceAboutPage:
    case AppLaunchSource::kSourceKeyboard:
    case AppLaunchSource::kSourceExtensionsPage:
    case AppLaunchSource::kSourceManagementApi:
    case AppLaunchSource::kSourceEphemeralAppDeprecated:
    case AppLaunchSource::kSourceBackground:
    case AppLaunchSource::kSourceKiosk:
    case AppLaunchSource::kSourceChromeInternal:
    case AppLaunchSource::kSourceTest:
    case AppLaunchSource::kSourceInstalledNotification:
    case AppLaunchSource::kSourceContextMenu:
    case AppLaunchSource::kSourceArc:
    case AppLaunchSource::kSourceIntentUrl:
      return static_cast<app_runtime::LaunchSource>(source);

    // We don't allow extensions to launch an app specifying
    // kSourceRunOnOsLogin, kSourceProtocolHandler or kSourceReparenting as the
    // source. In this case we map it to LaunchSource::kChromeInternal.
    case AppLaunchSource::kSourceRunOnOsLogin:
    case AppLaunchSource::kSourceProtocolHandler:
    case AppLaunchSource::kSourceReparenting:
      return app_runtime::LaunchSource::kChromeInternal;

    // New enumerators must be added here. Because the three previous entries in
    // AppLaunchSource are missing entries in LaunchSource, we need to subtract
    // three to remain in sync with LaunchSource.
    case AppLaunchSource::kSourceAppHomePage:
    case AppLaunchSource::kSourceFocusMode:
    case AppLaunchSource::kSourceSparky:
      return static_cast<app_runtime::LaunchSource>(
          base::to_underlying(source) - 3);
  }
}

}  // namespace

// static
void AppRuntimeEventRouter::DispatchOnEmbedRequestedEvent(
    content::BrowserContext* context,
    base::Value::Dict embed_app_data,
    const Extension* extension) {
  DispatchOnEmbedRequestedEventImpl(extension->id(), std::move(embed_app_data),
                                    context);
}

// static
void AppRuntimeEventRouter::DispatchOnLaunchedEvent(
    BrowserContext* context,
    const Extension* extension,
    extensions::AppLaunchSource source,
    std::optional<app_runtime::LaunchData> launch_data) {
  if (!launch_data) {
    launch_data.emplace();
  }
  app_runtime::LaunchSource source_enum = GetLaunchSourceEnum(source);
  if (extensions::FeatureSwitch::trace_app_source()->IsEnabled()) {
    launch_data->source = source_enum;
  }

  DispatchOnLaunchedEventImpl(extension->id(), source_enum,
                              launch_data->ToValue(), context);
}

// static
void AppRuntimeEventRouter::DispatchOnRestartedEvent(
    BrowserContext* context,
    const Extension* extension) {
  auto event = std::make_unique<Event>(events::APP_RUNTIME_ON_RESTARTED,
                                       app_runtime::OnRestarted::kEventName,
                                       base::Value::List(), context);
  EventRouter::Get(context)->DispatchEventToExtension(extension->id(),
                                                      std::move(event));
}

// static
void AppRuntimeEventRouter::DispatchOnLaunchedEventWithFileEntries(
    BrowserContext* context,
    const Extension* extension,
    extensions::AppLaunchSource source,
    const std::string& handler_id,
    const std::vector<EntryInfo>& entries,
    const std::vector<GrantedFileEntry>& file_entries) {
  app_runtime::LaunchSource source_enum = GetLaunchSourceEnum(source);

  // TODO(sergeygs): Use the same way of creating an event (using the generated
  // boilerplate) as below in DispatchOnLaunchedEventWithUrl.
  base::Value::Dict launch_data;
  launch_data.Set("id", handler_id);

  if (extensions::FeatureSwitch::trace_app_source()->IsEnabled()) {
    launch_data.Set("source", app_runtime::ToString(source_enum));
  }

  base::Value::List items;
  DCHECK(file_entries.size() == entries.size());
  for (size_t i = 0; i < file_entries.size(); ++i) {
    base::Value::Dict launch_item;

    // TODO: The launch item type should be documented in the idl so that this
    // entire function can be strongly typed and built using an
    // app_runtime::LaunchData instance.
    launch_item.Set("fileSystemId", file_entries[i].filesystem_id);
    launch_item.Set("baseName", file_entries[i].registered_name);
    launch_item.Set("mimeType", entries[i].mime_type);
    launch_item.Set("entryId", file_entries[i].id);
    launch_item.Set("isDirectory", entries[i].is_directory);
    items.Append(std::move(launch_item));
  }
  launch_data.Set("items", std::move(items));
  DispatchOnLaunchedEventImpl(extension->id(), source_enum,
                              std::move(launch_data), context);
}

// static
void AppRuntimeEventRouter::DispatchOnLaunchedEventWithUrl(
    BrowserContext* context,
    const Extension* extension,
    const std::string& handler_id,
    const GURL& url,
    const GURL& referrer_url) {
  app_runtime::LaunchData launch_data;
  app_runtime::LaunchSource source_enum =
      app_runtime::LaunchSource::kUrlHandler;
  launch_data.id = handler_id;
  launch_data.url = url.spec();
  launch_data.referrer_url = referrer_url.spec();
  if (extensions::FeatureSwitch::trace_app_source()->IsEnabled()) {
    launch_data.source = source_enum;
  }
  DispatchOnLaunchedEventImpl(extension->id(), source_enum,
                              launch_data.ToValue(), context);
}

}  // namespace extensions