blob: 1f1113b075b3c557bc2639af09a7046d6c681218 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_EXTENSION_REGISTRAR_H_
#define EXTENSIONS_BROWSER_EXTENSION_REGISTRAR_H_
#include <memory>
#include <set>
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/sync/model/string_ordinal.h"
#include "extensions/browser/blocklist_state.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/process_manager_observer.h"
#include "extensions/browser/unloaded_extension_reason.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
namespace base {
class CommandLine;
} // namespace base
namespace content {
class BrowserContext;
class DevToolsAgentHost;
} // namespace content
namespace extensions {
class DelayedInstallManager;
class Extension;
class ExtensionHost;
class ExtensionPrefs;
class ExtensionRegistry;
class ExtensionSystem;
class RendererStartupHelper;
// ExtensionRegistrar drives the stages of registering and unregistering
// extensions for a BrowserContext. It uses the ExtensionRegistry to track
// extension states. Other classes may query the ExtensionRegistry directly,
// but eventually only ExtensionRegistrar will be able to make changes to it.
class ExtensionRegistrar : public KeyedService, public ProcessManagerObserver {
public:
// Delegate for embedder-specific functionality like policy and permissions.
class Delegate {
public:
Delegate() = default;
Delegate(const Delegate&) = delete;
Delegate& operator=(const Delegate&) = delete;
virtual ~Delegate() = default;
// Called before `extension` is added. `old_extension` is the extension
// being replaced, in the case of a reload or upgrade.
virtual void PreAddExtension(const Extension* extension,
const Extension* old_extension) = 0;
// Handles extension install tasks before AddExtension.
virtual void OnAddNewOrUpdatedExtension(const Extension* extension) = 0;
// Handles updating the browser context when an extension is activated
// (becomes enabled).
virtual void PostActivateExtension(
scoped_refptr<const Extension> extension) = 0;
// Handles updating the browser context when an enabled extension is
// deactivated (whether disabled or removed).
virtual void PostDeactivateExtension(
scoped_refptr<const Extension> extension) = 0;
// Called before `extension` is uninstalled. Performs the operations
// necessary before `extension` is uninstalled.
virtual void PreUninstallExtension(
scoped_refptr<const Extension> extension) = 0;
// Called after `extension` is uninstalled. Performs the operations to
// clean up the extensions files, etc.
virtual void PostUninstallExtension(
scoped_refptr<const Extension> extension,
base::OnceClosure done_callback) = 0;
// Given an extension ID and/or path, loads that extension as a reload with
// noisy load error behavior.
virtual void LoadExtensionForReload(const ExtensionId& extension_id,
const base::FilePath& path) = 0;
// Given an extension ID and/or path, loads that extension as a reload with
// quiet load error behavior.
virtual void LoadExtensionForReloadWithQuietFailure(
const ExtensionId& extension_id,
const base::FilePath& path) = 0;
// Informs the user that an extension was disabled after upgrading to higher
// permissions. If `is_remote_install` is true, the extension was disabled
// because it was installed remotely.
virtual void ShowExtensionDisabledError(const Extension* extension,
bool is_remote_install) = 0;
// Returns true if the extension is allowed to be enabled or disabled,
// respectively.
virtual bool CanEnableExtension(const Extension* extension) = 0;
virtual bool CanDisableExtension(const Extension* extension) = 0;
// Updates the `extension`s granted permissions lists to include all
// permissions in the `extensions`s manifest.
virtual void GrantActivePermissions(const Extension* extension) = 0;
// Checks if there are any new external extensions to notify the user about.
virtual void UpdateExternalExtensionAlert() = 0;
// Informs the service that an extension's files are in place for loading.
//
// `extension` the extension
// `page_ordinal` the location of the extension in the app
// launcher
// `install_flags` a bitmask of InstallFlags
// `ruleset_install_prefs` Install prefs needed for the Declarative Net
// Request API.
virtual void OnExtensionInstalled(
const Extension* extension,
const syncer::StringOrdinal& page_ordinal,
int install_flags,
base::Value::Dict ruleset_install_prefs) = 0;
};
explicit ExtensionRegistrar(content::BrowserContext* browser_context);
ExtensionRegistrar(const ExtensionRegistrar&) = delete;
ExtensionRegistrar& operator=(const ExtensionRegistrar&) = delete;
~ExtensionRegistrar() override;
// Returns the instance for the given `browser_context`.
static ExtensionRegistrar* Get(content::BrowserContext* browser_context);
// The provided `delegate` should outlive this object. May be called multiple
// times, for example to reset the delegate in tests.
void Init(Delegate* delegate,
bool extensions_enabled,
const base::CommandLine* command_line,
const base::FilePath& install_directory,
const base::FilePath& unpacked_install_directory);
// Returns true if the registrar has been initialized.
bool IsInitialized() const;
// Returns a weak pointer to `this`.
base::WeakPtr<ExtensionRegistrar> GetWeakPtr();
// KeyedService overrides:
// Called when the associated Profile is going to be destroyed.
void Shutdown() override;
// Adds the extension to the ExtensionRegistry. The extension will be added to
// the enabled, disabled, blocklisted or blocked set. If the extension is
// added as enabled, it will be activated.
void AddExtension(scoped_refptr<const Extension> extension);
// Updates preferences for a new or updated extension; notifies observers that
// the extension is installed, e.g., to update event handlers on background
// pages; and performs other extension install tasks before calling
// AddExtension.
// `install_flags` is a bitmask of InstallFlags.
void AddNewOrUpdatedExtension(const Extension* extension,
const base::flat_set<int>& disable_reasons,
int install_flags,
const syncer::StringOrdinal& page_ordinal,
const std::string& install_parameter,
base::Value::Dict ruleset_install_prefs);
// Informs the service that an extension's files are in place for loading.
//
// `extension` the extension
// `page_ordinal` the location of the extension in the app
// launcher
// `install_flags` a bitmask of InstallFlags
// `ruleset_install_prefs` Install prefs needed for the Declarative Net
// Request API.
void OnExtensionInstalled(const Extension* extension,
const syncer::StringOrdinal& page_ordinal,
int install_flags,
base::Value::Dict ruleset_install_prefs = {});
void OnExtensionInstalled(const Extension* extension,
const syncer::StringOrdinal& page_ordinal) {
OnExtensionInstalled(extension, page_ordinal,
static_cast<int>(kInstallFlagNone));
}
// Removes `extension` from the extension system by deactivating it if it is
// enabled and removing references to it from the ExtensionRegistry's
// enabled, disabled or terminated sets.
// Note: Extensions will not be removed from other sets (blocklisted or
// blocked). ExtensionService handles that, since it also adds it to those
// sets. TODO(michaelpg): Make ExtensionRegistrar the sole mutator of
// ExtensionRegistry to simplify this usage.
void RemoveExtension(const ExtensionId& extension_id,
UnloadedExtensionReason reason);
// If the extension is disabled, marks it as enabled and activates it for use.
// Otherwise, simply updates the ExtensionPrefs. (Blocklisted or blocked
// extensions cannot be enabled.)
void EnableExtension(const ExtensionId& extension_id);
// Marks `extension` as disabled and deactivates it. The ExtensionRegistry
// retains a reference to it, so it can be enabled later.
void DisableExtension(const ExtensionId& extension_id,
const DisableReasonSet& disable_reasons);
// Any code which needs to write unknown reasons should use the
// methods below, which operate on raw integers. This is needed for scenarios
// like Sync where unknown reasons can be synced from newer versions of the
// browser to older versions. The method above will trigger undefined behavior
// when unknown values are casted to DisableReason while constructing
// DisableReasonSet. Most code should use the method above. We want to limit
// the usage of the method below, so it is guarded by a passkey.
void DisableExtensionWithRawReasons(
ExtensionPrefs::DisableReasonRawManipulationPasskey,
const ExtensionId& extension_id,
base::flat_set<int> disable_reasons);
// Same as `DisableExtension`, but assumes that the request to disable
// `extension_id` originates from `source_extension` when evaluating whether
// the extension can be disabled. Please see `ExtensionMayModifySettings`
// for details.
void DisableExtensionWithSource(const Extension* source_extension,
const ExtensionId& extension_id,
disable_reason::DisableReason disable_reason);
// Helper to get the disable reasons for an installed (or upgraded) extension.
// Returning an empty set indicates that we should enable this extension
// initially.
base::flat_set<int> GetDisableReasonsOnInstalled(const Extension* extension);
// Attempts to enable all disabled extensions which the only disabled reason
// is reloading.
void EnabledReloadableExtensions();
// Check if we have preferences for the component extension and, if not or if
// the stored version differs, install the extension (without requirements
// checking) before calling AddExtension.
void AddComponentExtension(const Extension* extension);
// Removes the specified component extension.
void RemoveComponentExtension(const std::string& extension_id);
// Removes the disable reason and enable the extension if there are no disable
// reasons left and is not blocked for another reason.
void RemoveDisableReasonAndMaybeEnable(const std::string& extension_id,
disable_reason::DisableReason reason);
// Attempts to reload extension with noisy failures.
void ReloadExtension(const ExtensionId& extension_id);
// Attempts to reload extension with suppressing noisy failures.
void ReloadExtensionWithQuietFailure(const ExtensionId& extension_id);
// Uninstalls the specified extension. Callers should only call this method
// with extensions that exist. `reason` lets the caller specify why the
// extension is uninstalled.
// Note: this method synchronously removes the extension from the
// set of installed extensions stored in the ExtensionRegistry, but will
// asynchronously remove site-related data and the files stored on disk.
// Returns true if an uninstall was successfully triggered; this can fail if
// the extension cannot be uninstalled (such as a policy force-installed
// extension).
// `done_callback` is synchronously invoked once the site-related data and the
// files stored on disk are removed. If such a callback is not needed, pass in
// a null callback (base::NullCallback()).
bool UninstallExtension(
const std::string& extension_id,
UninstallReason reason,
std::u16string* error,
base::OnceClosure done_callback = base::NullCallback());
// Uninstalls extensions that have been migrated to component extensions.
void UninstallMigratedExtensions(base::span<const char* const> migrated_ids);
// Finishes installing `extension` and notifying the observers.
void FinishInstallation(const Extension* extension);
// Helper method to determine if an extension can be blocked.
bool CanBlockExtension(const Extension* extension) const;
// Puts all extensions in a blocked state: Unloading every extension, and
// preventing them from ever loading until UnblockAllExtensions is called.
// This state is stored in preferences, so persists until Chrome restarts.
//
// Component, external component and allowlisted policy installed extensions
// are exempt from being Blocked (see CanBlockExtension in .cc file).
void BlockAllExtensions();
// All blocked extensions are reverted to their previous state, and are
// reloaded. Newly added extensions are no longer automatically blocked.
void UnblockAllExtensions();
// Takes Safe Browsing and Omaha malware blocklist states into account and
// decides whether to remove the extension from the blocklist and reload it.
// Called when a blocklisted extension is removed from the Safe Browsing
// malware blocklist or Omaha malware blocklist. Also clears the acknowledged
// state if the extension is reloaded.
void OnBlocklistStateRemoved(const std::string& extension_id);
// Takes acknowledged malware blocklist state into account and decides whether
// to add the extension to the blocklist and unload it. Called when the
// extension is added to the Safe Browsing malware blocklist or the Omaha
// malware blocklist.
void OnBlocklistStateAdded(const std::string& extension_id);
// Takes Safe Browsing and Omaha blocklist states into account and decides
// whether to remove greylist disabled reason. Called when a greylisted
// state is removed from the Safe Browsing blocklist or Omaha blocklist. Also
// clears all acknowledged states if the greylist disabled reason is removed.
void OnGreylistStateRemoved(const std::string& extension_id);
// Takes acknowledged blocklist states into account and decides whether to
// disable the greylisted extension. Called when a new greylisted state is
// added to the Safe Browsing blocklist or Omaha blocklist.
void OnGreylistStateAdded(const std::string& extension_id,
BitMapBlocklistState new_state);
// Simulates an extension being blocklisted for tests.
void BlocklistExtensionForTest(const std::string& extension_id);
// Simulates an extension being greylisted for tests.
void GreylistExtensionForTest(const std::string& extension_id,
const BitMapBlocklistState& state);
// Deactivates the extension, adding its id to the list of terminated
// extensions.
void TerminateExtension(const ExtensionId& extension_id);
// Removes the extension from the terminated list. TODO(michaelpg): Make a
// private implementation detail when no longer called from ExtensionService.
void UntrackTerminatedExtension(const ExtensionId& extension_id);
// Returns true if the extension is enabled (including terminated), or if it
// is not loaded but isn't explicitly disabled in preferences.
bool IsExtensionEnabled(const ExtensionId& extension_id) const;
// Called after the renderer main frame for the background page with the
// associated host is created.
void DidCreateMainFrameForBackgroundPage(ExtensionHost* host);
void OnUnpackedExtensionReloadFailed(const base::FilePath& path);
// Updates the `extension`s granted permissions lists to include all
// permissions in the `extension`s manifest and re-enables the
// extension.
void GrantPermissionsAndEnableExtension(const Extension& extension);
// Adds to the set of allowlisted enabled extensions loaded from the
// --disable-extensions-except command line flag.
void AddDisableFlagExemptedExtension(const ExtensionId& extension_id);
// Simple accessors.
bool extensions_enabled() const { return extensions_enabled_; }
bool block_extensions() const { return block_extensions_; }
const base::FilePath& install_directory() const { return install_directory_; }
const base::FilePath& unpacked_install_directory() const {
return unpacked_install_directory_;
}
void set_extensions_enabled_for_test(bool value) {
extensions_enabled_ = value;
}
private:
// How to surface an extension load error, e.g. showing an error dialog. The
// actual behavior is up to the embedder.
enum class LoadErrorBehavior {
kQuiet = 0, // Just log the error.
kNoisy, // Show an error dialog.
};
// Adds the extension to the appropriate registry set, based on ExtensionPrefs
// and our `delegate_`. Activates the extension if it's added to the enabled
// set.
void AddNewExtension(scoped_refptr<const Extension> extension);
// Activates `extension` by marking it enabled and notifying other components
// about it.
void ActivateExtension(const Extension* extension, bool is_newly_added);
// Triggers the unloaded notifications to deactivate an extension.
void DeactivateExtension(const Extension* extension,
UnloadedExtensionReason reason);
// Attempts to reload the specified extension by disabling it if it is enabled
// and requesting the Delegate load it again.
// Because reloading can invalidate a reference to the ID. So make a copy of
// `extension_id` first by passing value and retrieve a new Extension pointer
// afterwards.
void DoReloadExtension(ExtensionId extension_id,
LoadErrorBehavior load_error_behavior);
// Unregister the service worker that is not from manifest and has extension
// root scope.
void UnregisterServiceWorkerWithRootScope(const Extension* extension);
void NotifyServiceWorkerUnregistered(const ExtensionId& extension_id,
bool worker_previously_registered,
blink::ServiceWorkerStatusCode status);
// Given an extension that was disabled for reloading, completes the reload
// by replacing the old extension with the new version and enabling it.
// Returns true on success.
bool ReplaceReloadedExtension(scoped_refptr<const Extension> extension);
// Upon reloading an extension, spins up its context if necessary.
void MaybeSpinUpLazyContext(const Extension* extension, bool is_newly_added);
// ProcessManagerObserver overrides
void OnStartedTrackingServiceWorkerInstance(
const WorkerId& worker_id) override;
// Returns true if `extension` can be added.
bool CanAddExtension(const Extension* extension) const;
// Returns true if `extension` should be blocked.
bool ShouldBlockExtension(const Extension* extension) const;
const raw_ptr<content::BrowserContext> browser_context_;
// Delegate provided by SetDelegate. Should outlive this object.
raw_ptr<Delegate> delegate_;
// Whether or not extensions are enabled.
bool extensions_enabled_ = true;
// The full path to the directory where extensions are installed.
base::FilePath install_directory_;
// The full path to the directory where unpacked (e.g. from .zip files)
// extensions are installed.
base::FilePath unpacked_install_directory_;
// Keyed services we depend on. Cached here for repeated access.
// TODO(crbug.com/398014892): Figure out a way to break the dependency
// between ExtensionRegistrar and ExtensionSystem.
raw_ptr<ExtensionSystem> extension_system_;
const raw_ptr<ExtensionPrefs> extension_prefs_;
const raw_ptr<ExtensionRegistry> registry_;
const raw_ptr<RendererStartupHelper> renderer_helper_;
raw_ptr<DelayedInstallManager> delayed_install_manager_ = nullptr;
// Map of DevToolsAgentHost instances that are detached,
// waiting for an extension to be reloaded.
using OrphanedDevTools =
std::map<std::string,
std::vector<scoped_refptr<content::DevToolsAgentHost>>>;
OrphanedDevTools orphaned_dev_tools_;
// Map unloaded extensions' ids to their paths. When a temporarily loaded
// extension is unloaded, we lose the information about it and don't have
// any in the extension preferences file.
using UnloadedExtensionPathMap = std::map<ExtensionId, base::FilePath>;
UnloadedExtensionPathMap unloaded_extension_paths_;
// Store the ids of reloading extensions. We use this to re-enable extensions
// which were disabled for a reload.
ExtensionIdSet reloading_extensions_;
// Store the paths of extensions that failed to reload. We use this to retry
// reload.
std::set<base::FilePath> failed_to_reload_unpacked_extensions_;
// Set of allowlisted enabled extensions loaded from the
// --disable-extensions-except command line flag.
std::set<ExtensionId> disable_flag_exempted_extensions_;
// Set to true if extensions are all to be blocked.
bool block_extensions_ = false;
base::ScopedObservation<ProcessManager, ProcessManagerObserver>
process_manager_observation_{this};
base::WeakPtrFactory<ExtensionRegistrar> weak_factory_{this};
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_EXTENSION_REGISTRAR_H_