diff --git a/page_manager.services.yml b/page_manager.services.yml
index 43f99ab..3874bc0 100644
--- a/page_manager.services.yml
+++ b/page_manager.services.yml
@@ -1,14 +1,20 @@
 services:
-  page_manager.current_user_context:
-    class: Drupal\page_manager\EventSubscriber\CurrentUserContext
-    arguments: ['@current_user', '@entity_type.manager']
-    tags:
-      - { name: 'event_subscriber' }
+  # Missing argument is purposeful, see \Drupal\page_manager\ContextProvidersPass.
+  page_manager.context_repository:
+    class: Drupal\page_manager\Context\PageContextRepository
+    arguments: ['@service_container']
+
   page_manager.route_param_context:
-    class: Drupal\page_manager\EventSubscriber\RouteParamContext
+    class: Drupal\page_manager\ContextProvider\RouteParamContext
     arguments: ['@router.route_provider', '@request_stack']
     tags:
-      - { name: 'event_subscriber' }
+      - { name: 'page_manager_context_provider' }
+  page_manager.static_context_provider:
+    class: Drupal\page_manager\ContextProvider\StaticContextProvider
+    arguments: ['@page_manager.context_mapper']
+    tags:
+      - { name: 'page_manager_context_provider' }
+
   page_manager.context_mapper:
     class: Drupal\page_manager\ContextMapper
     arguments: ['@entity.repository']
diff --git a/page_manager_ui/src/Controller/PageManagerController.php b/page_manager_ui/src/Controller/PageManagerController.php
index 708d0e6..a0aff6f 100644
--- a/page_manager_ui/src/Controller/PageManagerController.php
+++ b/page_manager_ui/src/Controller/PageManagerController.php
@@ -246,7 +246,7 @@ public function selectAccessCondition(PageInterface $page) {
       '#theme' => 'links',
       '#links' => [],
     ];
-    $available_plugins = $this->conditionManager->getDefinitionsForContexts($page->getContexts());
+    $available_plugins = $this->conditionManager->getDefinitionsForContexts($page->getAvailableContexts());
     foreach ($available_plugins as $access_id => $access_condition) {
       $build['#links'][$access_id] = [
         'title' => $access_condition['label'],
@@ -274,7 +274,7 @@ public function selectSelectionCondition(PageVariantInterface $page_variant) {
       '#theme' => 'links',
       '#links' => [],
     ];
-    $available_plugins = $this->conditionManager->getDefinitionsForContexts($page_variant->getContexts());
+    $available_plugins = $this->conditionManager->getDefinitionsForContexts($page_variant->getAvailableContexts());
     foreach ($available_plugins as $selection_id => $selection_condition) {
       $build['#links'][$selection_id] = [
         'title' => $selection_condition['label'],
@@ -310,7 +310,7 @@ public function selectBlock(Request $request, PageVariantInterface $page_variant
         ],
       ],
     ];
-    $available_plugins = $this->blockManager->getDefinitionsForContexts($page_variant->getContexts());
+    $available_plugins = $this->blockManager->getDefinitionsForContexts($page_variant->getAvailableContexts());
     // Order by category, and then by admin label.
     $available_plugins = $this->blockManager->getSortedDefinitions($available_plugins);
     foreach ($available_plugins as $plugin_id => $plugin_definition) {
diff --git a/page_manager_ui/src/Form/AccessConditionFormBase.php b/page_manager_ui/src/Form/AccessConditionFormBase.php
index 26b5ead..25f951e 100644
--- a/page_manager_ui/src/Form/AccessConditionFormBase.php
+++ b/page_manager_ui/src/Form/AccessConditionFormBase.php
@@ -27,7 +27,7 @@
    */
   public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $condition_id = NULL) {
     $this->page = $page;
-    return parent::buildForm($form, $form_state, $condition_id, $page->getContexts());
+    return parent::buildForm($form, $form_state, $condition_id, $page->getAvailableContexts());
   }
 
   /**
diff --git a/page_manager_ui/src/Form/PageVariantEditForm.php b/page_manager_ui/src/Form/PageVariantEditForm.php
index 3cf0992..edb5576 100644
--- a/page_manager_ui/src/Form/PageVariantEditForm.php
+++ b/page_manager_ui/src/Form/PageVariantEditForm.php
@@ -334,7 +334,7 @@ protected function buildContextForm() {
       ],
       '#empty' => $this->t('There is no available context.'),
     ];
-    $contexts = $page_variant->getContexts();
+    $contexts = $page_variant->getAvailableContexts();
     foreach ($contexts as $name => $context) {
       $context_definition = $context->getContextDefinition();
 
@@ -351,13 +351,13 @@ protected function buildContextForm() {
 
       // Add operation links if the context is a static context.
       $operations = [];
-      if ($page_variant->getStaticContext($name)) {
+      if ($static_context_name = $this->deriveStaticContextName($name)) {
         $operations['edit'] = [
           'title' => $this->t('Edit'),
           'url' => Url::fromRoute('page_manager.static_context_edit', [
             'page' => $page_variant->get('page'),
             'page_variant' => $page_variant->id(),
-            'name' => $name,
+            'name' => $static_context_name,
           ]),
           'attributes' => $attributes,
         ];
@@ -366,7 +366,7 @@ protected function buildContextForm() {
           'url' => Url::fromRoute('page_manager.static_context_delete', [
             'page' => $page_variant->get('page'),
             'page_variant' => $page_variant->id(),
-            'name' => $name,
+            'name' => $static_context_name,
           ]),
           'attributes' => $attributes,
         ];
@@ -379,7 +379,16 @@ protected function buildContextForm() {
       $form['available_context'][$name] = $row;
     }
     return $form;
+  }
 
+  /**
+   * @todo.
+   */
+  protected function deriveStaticContextName($name) {
+    $static_context_prefix = '@page_manager.static_context_provider:';
+    if (strpos($name, $static_context_prefix) === 0) {
+      return substr($name, strlen($static_context_prefix));
+    }
   }
 
   /**
diff --git a/page_manager_ui/src/Form/SelectionConditionFormBase.php b/page_manager_ui/src/Form/SelectionConditionFormBase.php
index e1087ff..72a2443 100644
--- a/page_manager_ui/src/Form/SelectionConditionFormBase.php
+++ b/page_manager_ui/src/Form/SelectionConditionFormBase.php
@@ -27,7 +27,7 @@
    */
   public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $condition_id = NULL) {
     $this->pageVariant = $page_variant;
-    return parent::buildForm($form, $form_state, $condition_id, $page_variant->getContexts());
+    return parent::buildForm($form, $form_state, $condition_id, $page_variant->getAvailableContexts());
   }
 
   /**
diff --git a/page_manager_ui/src/Form/StaticContextFormBase.php b/page_manager_ui/src/Form/StaticContextFormBase.php
index b3a9091..91926d0 100644
--- a/page_manager_ui/src/Form/StaticContextFormBase.php
+++ b/page_manager_ui/src/Form/StaticContextFormBase.php
@@ -227,7 +227,7 @@ protected function getEntityFromSelection($entity_type, $selection) {
    *   TRUE if the format exists, FALSE otherwise.
    */
   public function contextExists($name) {
-    return isset($this->pageVariant->getContexts()[$name]);
+    return isset($this->pageVariant->getAvailableContexts()[$name]);
   }
 
   /**
diff --git a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php
index 7d440ef..74928a1 100644
--- a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php
+++ b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php
@@ -78,7 +78,7 @@ public function buildForm(array $form, FormStateInterface $form_state, PageVaria
     ];
 
     if ($this->block instanceof ContextAwarePluginInterface) {
-      $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->pageVariant->getContexts());
+      $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->pageVariant->getAvailableContexts());
     }
 
     $form['actions']['submit'] = [
diff --git a/src/Context/PageContextRepository.php b/src/Context/PageContextRepository.php
new file mode 100644
index 0000000..3a3114d
--- /dev/null
+++ b/src/Context/PageContextRepository.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\Context\PageContextRepository.
+ */
+
+namespace Drupal\page_manager\Context;
+
+use Drupal\Core\Plugin\Context\LazyContextRepository;
+use Drupal\page_manager\ContextProvider\PageVariantContextProviderInterface;
+use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
+
+/**
+ * Provides a context repository that takes a page or page variant into account.
+ *
+ * This duplicates \Drupal\Core\Plugin\Context\LazyContextRepository in order to
+ * pass the page along to the context providers.
+ */
+class PageContextRepository extends LazyContextRepository implements PageContextRepositoryInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRuntimeContexts(array $context_ids, PageVariantInterface $page_variant = NULL) {
+    $contexts = [];
+
+    // Create a map of context providers (service IDs) to unqualified context
+    // IDs.
+    $context_ids_by_service = [];
+    foreach ($context_ids as $id) {
+      if (isset($this->contexts[$id])) {
+        $contexts[$id] = $this->contexts[$id];
+        continue;
+      }
+      // The IDs have been passed in @{service_id}:{unqualified_context_id}
+      // format.
+      // @todo Convert to an assert once https://www.drupal.org/node/2408013 is
+      //   in.
+      if ($id[0] === '@' && strpos($id, ':') !== FALSE) {
+        list($service_id, $unqualified_context_id) = explode(':', $id, 2);
+        // Remove the leading '@'.
+        $service_id = substr($service_id, 1);
+      }
+      else {
+        throw new \InvalidArgumentException('You must provide the context IDs in the @{service_id}:{unqualified_context_id} format.');
+      }
+      $context_ids_by_service[$service_id][] = $unqualified_context_id;
+    }
+
+    // Iterate over all missing context providers (services), gather the
+    // runtime contexts and assign them as requested.
+    foreach ($context_ids_by_service as $service_id => $unqualified_context_ids) {
+      $service = $this->container->get($service_id);
+      if ($service instanceof PageVariantContextProviderInterface) {
+        $contexts_by_service = $service->getRuntimeContexts($unqualified_context_ids, $page_variant);
+      }
+      else {
+        $contexts_by_service = $service->getRuntimeContexts($unqualified_context_ids);
+      }
+      $wanted_contexts = array_intersect_key($contexts_by_service, array_flip($unqualified_context_ids));
+      foreach ($wanted_contexts as $unqualified_context_id => $context) {
+        $context_id = '@' . $service_id . ':' . $unqualified_context_id;
+        $this->contexts[$context_id] = $contexts[$context_id] = $context;
+      }
+    }
+
+    return $contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableContexts(PageVariantInterface $page_variant = NULL) {
+    $contexts = [];
+    foreach ($this->contextProviderServiceIDs as $service_id) {
+      $service = $this->container->get($service_id);
+      if ($service instanceof PageVariantContextProviderInterface) {
+        $contexts_by_service = $service->getAvailableContexts($page_variant);
+      }
+      else {
+        $contexts_by_service = $service->getAvailableContexts();
+      }
+      foreach ($contexts_by_service as $unqualified_context_id => $context) {
+        $context_id = '@' . $service_id . ':' . $unqualified_context_id;
+        $contexts[$context_id] = $context;
+      }
+    }
+
+    return $contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailablePageOnlyContexts(PageInterface $page) {
+    $contexts = [];
+    foreach ($this->contextProviderServiceIDs as $service_id) {
+      $service = $this->container->get($service_id);
+      if ($service instanceof PageVariantContextProviderInterface) {
+        $contexts_by_service = $service->getAvailablePageOnlyContexts($page);
+      }
+      else {
+        $contexts_by_service = $service->getAvailableContexts();
+      }
+      foreach ($contexts_by_service as $unqualified_context_id => $context) {
+        $context_id = '@' . $service_id . ':' . $unqualified_context_id;
+        $contexts[$context_id] = $context;
+      }
+    }
+
+    return $contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRuntimePageOnlyContexts(array $context_ids, PageInterface $page = NULL) {
+    $contexts = [];
+
+    // Create a map of context providers (service IDs) to unqualified context
+    // IDs.
+    $context_ids_by_service = [];
+    foreach ($context_ids as $id) {
+      if (isset($this->contexts[$id])) {
+        $contexts[$id] = $this->contexts[$id];
+        continue;
+      }
+      // The IDs have been passed in @{service_id}:{unqualified_context_id}
+      // format.
+      // @todo Convert to an assert once https://www.drupal.org/node/2408013 is
+      //   in.
+      if ($id[0] === '@' && strpos($id, ':') !== FALSE) {
+        list($service_id, $unqualified_context_id) = explode(':', $id, 2);
+        // Remove the leading '@'.
+        $service_id = substr($service_id, 1);
+      }
+      else {
+        throw new \InvalidArgumentException('You must provide the context IDs in the @{service_id}:{unqualified_context_id} format.');
+      }
+      $context_ids_by_service[$service_id][] = $unqualified_context_id;
+    }
+
+    // Iterate over all missing context providers (services), gather the
+    // runtime contexts and assign them as requested.
+    foreach ($context_ids_by_service as $service_id => $unqualified_context_ids) {
+      $service = $this->container->get($service_id);
+      if ($service instanceof PageVariantContextProviderInterface) {
+        $contexts_by_service = $service->getRuntimePageOnlyContexts($unqualified_context_ids, $page);
+      }
+      else {
+        $contexts_by_service = $service->getRuntimeContexts($unqualified_context_ids);
+      }
+      $wanted_contexts = array_intersect_key($contexts_by_service, array_flip($unqualified_context_ids));
+      foreach ($wanted_contexts as $unqualified_context_id => $context) {
+        $context_id = '@' . $service_id . ':' . $unqualified_context_id;
+        $this->contexts[$context_id] = $contexts[$context_id] = $context;
+      }
+    }
+
+    return $contexts;
+  }
+
+}
diff --git a/src/Context/PageContextRepositoryInterface.php b/src/Context/PageContextRepositoryInterface.php
new file mode 100644
index 0000000..934afe3
--- /dev/null
+++ b/src/Context/PageContextRepositoryInterface.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\Context\PageContextRepositoryInterface.
+ */
+
+namespace Drupal\page_manager\Context;
+
+use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
+use Drupal\page_manager\PageInterface;
+
+/**
+ * Provides a context repository that takes a page or page variant into account.
+ */
+interface PageContextRepositoryInterface extends ContextRepositoryInterface{
+
+  /**
+   * @todo.
+   *
+   * @param array $context_ids
+   * @param \Drupal\page_manager\PageInterface $page
+   *
+   * @return
+   */
+  public function getRuntimePageOnlyContexts(array $context_ids, PageInterface $page);
+
+  /**
+   * @todo.
+   *
+   * @param \Drupal\page_manager\PageInterface $page
+   *
+   * @return
+   */
+  public function getAvailablePageOnlyContexts(PageInterface $page);
+
+}
diff --git a/src/ContextProvider/PageVariantContextProviderInterface.php b/src/ContextProvider/PageVariantContextProviderInterface.php
new file mode 100644
index 0000000..b8850be
--- /dev/null
+++ b/src/ContextProvider/PageVariantContextProviderInterface.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\ContextProvider\PageVariantContextProviderInterface.
+ */
+
+namespace Drupal\page_manager\ContextProvider;
+
+use Drupal\Core\Plugin\Context\ContextProviderInterface;
+use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
+
+/**
+ * Defines an interface for providing plugin contexts that require a page.
+ */
+interface PageVariantContextProviderInterface extends ContextProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param \Drupal\page_manager\PageVariantInterface $page_variant
+   *   The page variant to get contexts for.
+   */
+  public function getRuntimeContexts(array $unqualified_context_ids, PageVariantInterface $page_variant = NULL);
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param \Drupal\page_manager\PageVariantInterface $page_variant
+   *   The page variant to get contexts for.
+   */
+  public function getAvailableContexts(PageVariantInterface $page_variant = NULL);
+
+  /**
+   * @todo.
+   *
+   * @param \Drupal\page_manager\PageInterface $page
+   *
+   * @return
+   */
+  public function getAvailablePageOnlyContexts(PageInterface $page);
+
+  /**
+   * @todo.
+   *
+   * @param \Drupal\page_manager\PageInterface $page
+   *
+   * @return
+   */
+  public function getRuntimePageOnlyContexts(array $unqualified_context_ids, PageInterface $page);
+
+}
diff --git a/src/EventSubscriber/RouteParamContext.php b/src/ContextProvider/RouteParamContext.php
similarity index 65%
rename from src/EventSubscriber/RouteParamContext.php
rename to src/ContextProvider/RouteParamContext.php
index 2f90c7b..5a44550 100644
--- a/src/EventSubscriber/RouteParamContext.php
+++ b/src/ContextProvider/RouteParamContext.php
@@ -2,25 +2,24 @@
 
 /**
  * @file
- * Contains \Drupal\page_manager\EventSubscriber\RouteParamContext.
+ * Contains \Drupal\page_manager\ContextProvider\RouteParamContext.
  */
 
-namespace Drupal\page_manager\EventSubscriber;
+namespace Drupal\page_manager\ContextProvider;
 
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\page_manager\Event\PageManagerContextEvent;
 use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Routing\RouteProviderInterface;
-use Drupal\page_manager\Event\PageManagerEvents;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * Sets values from the route parameters as a context.
  */
-class RouteParamContext implements EventSubscriberInterface {
+class RouteParamContext implements PageVariantContextProviderInterface {
 
   use StringTranslationTrait;
 
@@ -39,7 +38,7 @@ class RouteParamContext implements EventSubscriberInterface {
   protected $requestStack;
 
   /**
-   * Constructs a new CurrentUserContext.
+   * Constructs a new RouteParamContext.
    *
    * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
    *   The route provider.
@@ -52,17 +51,23 @@ public function __construct(RouteProviderInterface $route_provider, RequestStack
   }
 
   /**
-   * Adds in the current user as a context.
-   *
-   * @param \Drupal\page_manager\Event\PageManagerContextEvent $event
-   *   The page entity context event.
+   * {@inheritdoc}
    */
-  public function onPageContext(PageManagerContextEvent $event) {
+  public function getRuntimeContexts(array $unqualified_context_ids, PageVariantInterface $page_variant = NULL) {
+    if (!$page_variant) {
+      // @todo Add an assert here.
+      return [];
+    }
+
+    return $this->getPageContexts($page_variant->getPage());
+  }
+
+  protected function getPageContexts(PageInterface $page) {
     $request = $this->requestStack->getCurrentRequest();
-    $page = $event->getPage();
     $routes = $this->routeProvider->getRoutesByPattern($page->getPath())->all();
     $route = reset($routes);
 
+    $contexts = [];
     if ($route && $route_contexts = $route->getOption('parameters')) {
       foreach ($route_contexts as $route_context_name => $route_context) {
         // Skip this parameter.
@@ -85,17 +90,31 @@ public function onPageContext(PageManagerContextEvent $event) {
         $context = new Context(new ContextDefinition($route_context['type'], $context_name, FALSE), $value);
         $context->addCacheableDependency($cacheability);
 
-        $page->addContext($route_context_name, $context);
+        $contexts[$route_context_name] = $context;
       }
     }
+    return $contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableContexts(PageVariantInterface $page_variant = NULL) {
+    return $this->getRuntimeContexts([], $page_variant);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailablePageOnlyContexts(PageInterface $page) {
+    return $this->getPageContexts($page);
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function getSubscribedEvents() {
-    $events[PageManagerEvents::PAGE_CONTEXT][] = 'onPageContext';
-    return $events;
+  public function getRuntimePageOnlyContexts(array $unqualified_context_ids, PageInterface $page) {
+    return $this->getPageContexts($page);
   }
 
 }
diff --git a/src/ContextProvider/StaticContextProvider.php b/src/ContextProvider/StaticContextProvider.php
new file mode 100644
index 0000000..7a82882
--- /dev/null
+++ b/src/ContextProvider/StaticContextProvider.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\ContextProvider\StaticContextProvider.
+ */
+
+namespace Drupal\page_manager\ContextProvider;
+
+use Drupal\page_manager\ContextMapperInterface;
+use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
+
+/**
+ * @todo.
+ */
+class StaticContextProvider implements PageVariantContextProviderInterface {
+
+  /**
+   * @var \Drupal\page_manager\ContextMapperInterface
+   */
+  protected $contextMapper;
+
+  /**
+   * StaticContextProvider constructor.
+   *
+   * @param \Drupal\page_manager\ContextMapperInterface $context_mapper
+   */
+  public function __construct(ContextMapperInterface $context_mapper) {
+    $this->contextMapper = $context_mapper;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRuntimeContexts(array $unqualified_context_ids, PageVariantInterface $page_variant = NULL) {
+    if (!$page_variant) {
+      // @todo Add an assert here.
+      return [];
+    }
+
+    return $this->contextMapper->getContextValues($page_variant->getStaticContexts());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableContexts(PageVariantInterface $page_variant = NULL) {
+    return $this->getRuntimeContexts([], $page_variant);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailablePageOnlyContexts(PageInterface $page) {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRuntimePageOnlyContexts(array $unqualified_context_ids, PageInterface $page) {
+    return [];
+  }
+
+}
diff --git a/src/ContextProvidersPass.php b/src/ContextProvidersPass.php
new file mode 100644
index 0000000..7a2b184
--- /dev/null
+++ b/src/ContextProvidersPass.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\ContextProvidersPass.
+ */
+
+namespace Drupal\page_manager;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+/**
+ * Adds the context provider service IDs to the context manager.
+ */
+class ContextProvidersPass implements CompilerPassInterface {
+
+  /**
+   * {@inheritdoc}
+   *
+   * Passes the service IDs of all context providers to the context repository.
+   */
+  public function process(ContainerBuilder $container) {
+    $context_providers = [];
+    // Use a separate tag to prevent these classes from being called by core.
+    foreach (array_keys($container->findTaggedServiceIds('page_manager_context_provider')) as $id) {
+      $context_providers[] = $id;
+    }
+
+    // Ignore the node route context provider, it is superseded by the generic
+    // route params context provider.
+    $core_providers_to_skip = [
+      'node.node_route_context',
+    ];
+    foreach (array_keys($container->findTaggedServiceIds('context_provider')) as $id) {
+      if (!in_array($id, $core_providers_to_skip)) {
+        $context_providers[] = $id;
+      }
+    }
+
+    $definition = $container->getDefinition('page_manager.context_repository');
+    $definition->addArgument($context_providers);
+  }
+
+}
diff --git a/src/Entity/Page.php b/src/Entity/Page.php
index 49c5a8a..000b562 100644
--- a/src/Entity/Page.php
+++ b/src/Entity/Page.php
@@ -7,9 +7,7 @@
 
 namespace Drupal\page_manager\Entity;
 
-use Drupal\Component\Plugin\Context\ContextInterface;
-use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\page_manager\Event\PageManagerEvents;
+use Drupal\page_manager\ObjectWithContextAwarePluginCollectionTrait;
 use Drupal\page_manager\PageInterface;
 use Drupal\Core\Condition\ConditionPluginCollection;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
@@ -44,6 +42,8 @@
  */
 class Page extends ConfigEntityBase implements PageInterface {
 
+  use ObjectWithContextAwarePluginCollectionTrait;
+
   /**
    * The ID of the page entity.
    *
@@ -73,13 +73,6 @@ class Page extends ConfigEntityBase implements PageInterface {
   protected $variants;
 
   /**
-   * An array of collected contexts.
-   *
-   * @var \Drupal\Component\Plugin\Context\ContextInterface[]
-   */
-  protected $contexts = [];
-
-  /**
    * The configuration of access conditions.
    *
    * @var array
@@ -292,18 +285,16 @@ protected function filterParameters() {
   /**
    * {@inheritdoc}
    */
-  public function addContext($name, ContextInterface $value) {
-    $this->contexts[$name] = $value;
+  public function getRuntimeContexts() {
+    $context_ids = $this->getAllContextMappingIds($this);
+    return $this->contextRepository()->getRuntimePageOnlyContexts($context_ids, $this);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getContexts() {
-    if (!$this->contexts) {
-      $this->eventDispatcher()->dispatch(PageManagerEvents::PAGE_CONTEXT, new PageManagerContextEvent($this));
-    }
-    return $this->contexts;
+  public function getAvailableContexts() {
+    return $this->contextRepository()->getAvailablePageOnlyContexts($this);
   }
 
   /**
@@ -363,13 +354,13 @@ public function variantSortHelper($a, $b) {
   }
 
   /**
-   * Wraps the event dispatcher.
+   * Wraps the page context repository.
    *
-   * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
-   *   The event dispatcher.
+   * @return \Drupal\page_manager\Context\PageContextRepositoryInterface
+   *   The page context repository.
    */
-  protected function eventDispatcher() {
-    return \Drupal::service('event_dispatcher');
+  protected function contextRepository() {
+    return \Drupal::service('page_manager.context_repository');
   }
 
   /**
diff --git a/src/Entity/PageAccess.php b/src/Entity/PageAccess.php
index c4f4d1d..c28cc92 100644
--- a/src/Entity/PageAccess.php
+++ b/src/Entity/PageAccess.php
@@ -13,8 +13,8 @@
 use Drupal\Core\Entity\EntityHandlerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -74,8 +74,8 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
         return AccessResult::forbidden()->addCacheableDependency($entity);
       }
 
+      $contexts = $entity->getRuntimeContexts();
       $conditions = $entity->getAccessConditions();
-      $contexts = $entity->getContexts();
       foreach ($conditions as $condition) {
         if ($condition instanceof ContextAwarePluginInterface) {
           $this->contextHandler()->applyContextMapping($condition, $contexts);
diff --git a/src/Entity/PageVariant.php b/src/Entity/PageVariant.php
index f6b729c..1536567 100644
--- a/src/Entity/PageVariant.php
+++ b/src/Entity/PageVariant.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
+use Drupal\page_manager\ObjectWithContextAwarePluginCollectionTrait;
 use Drupal\page_manager\PageVariantInterface;
 
 /**
@@ -50,6 +51,8 @@
  */
 class PageVariant extends ConfigEntityBase implements PageVariantInterface {
 
+  use ObjectWithContextAwarePluginCollectionTrait;
+
   /**
    * The ID of the page variant entity.
    *
@@ -118,7 +121,14 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
    *
    * @var \Drupal\Component\Plugin\Context\ContextInterface[]|null
    */
-  protected $contexts = NULL;
+  protected $runtimeContexts = NULL;
+
+  /**
+   * An array of collected contexts.
+   *
+   * @var \Drupal\Component\Plugin\Context\ContextInterface[]|null
+   */
+  protected $availableContexts = NULL;
 
   /**
    * Static context references.
@@ -244,13 +254,22 @@ public function getPage() {
   /**
    * {@inheritdoc}
    */
-  public function getContexts() {
-    if (is_null($this->contexts)) {
-      $static_contexts = $this->getContextMapper()->getContextValues($this->getStaticContexts());
-      $page_contexts = $this->getPage()->getContexts();
-      $this->contexts = array_merge($static_contexts, $page_contexts);
+  public function getRuntimeContexts() {
+    if (is_null($this->runtimeContexts)) {
+      $context_mapping_ids = $this->getAllContextMappingIds($this) + $this->getAllContextMappingIds($this->getVariantPlugin());
+      $this->runtimeContexts = $this->contextRepository()->getRuntimeContexts($context_mapping_ids, $this);
     }
-    return $this->contexts;
+    return $this->runtimeContexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableContexts() {
+    if (is_null($this->availableContexts)) {
+      $this->availableContexts = $this->contextRepository()->getAvailableContexts($this);
+    }
+    return $this->availableContexts;
   }
 
   /**
@@ -259,7 +278,8 @@ public function getContexts() {
    * @return $this
    */
   protected function resetCollectedContexts() {
-    $this->contexts = NULL;
+    $this->runtimeContexts = NULL;
+    $this->availableContexts = NULL;
     return $this;
   }
 
@@ -406,12 +426,13 @@ protected function getConditionManager() {
   }
 
   /**
-   * Wraps the context mapper.
+   * Wraps the Page Manager context repository.
    *
-   * @return \Drupal\page_manager\ContextMapperInterface
+   * @return \Drupal\page_manager\Context\PageContextRepository
+   *   The Page Manager context repository.
    */
-  protected function getContextMapper() {
-    return \Drupal::service('page_manager.context_mapper');
+  protected function contextRepository() {
+    return \Drupal::service('page_manager.context_repository');
   }
 
   /**
@@ -428,8 +449,18 @@ protected function getPageStorage() {
    */
   public function __sleep() {
     $vars = parent::__sleep();
+
     // Gathered contexts objects should not be serialized.
-    unset($vars[array_search('contexts', $vars)]);
+    $unset_vars = [
+      'runtimeContexts',
+      'availableContexts',
+    ];
+    foreach ($unset_vars as $unset_var) {
+      if (!empty($this->{$unset_var})) {
+        unset($vars[array_search($unset_var, $vars)]);
+      }
+    }
+
     return $vars;
   }
 
diff --git a/src/Entity/PageVariantAccess.php b/src/Entity/PageVariantAccess.php
index 6c0fc09..b7287b3 100644
--- a/src/Entity/PageVariantAccess.php
+++ b/src/Entity/PageVariantAccess.php
@@ -68,10 +68,9 @@ protected function contextHandler() {
    * {@inheritdoc}
    */
   protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+    /** @var \Drupal\page_manager\PageVariantInterface $entity */
     if ($operation === 'view') {
-      /** @var \Drupal\page_manager\PageVariantInterface $entity */
-      $contexts = $entity->getContexts();
-
+      $contexts = $entity->getRuntimeContexts();
       $conditions = $entity->getSelectionConditions();
       foreach ($conditions as $condition) {
         if ($condition instanceof ContextAwarePluginInterface) {
@@ -81,7 +80,6 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
       return AccessResult::allowedIf($this->resolveConditions($conditions, $entity->getSelectionLogic()));
     }
     return parent::checkAccess($entity, $operation, $account);
-
   }
 
 }
diff --git a/src/Entity/PageVariantViewBuilder.php b/src/Entity/PageVariantViewBuilder.php
index c9b74d0..d9a5a52 100644
--- a/src/Entity/PageVariantViewBuilder.php
+++ b/src/Entity/PageVariantViewBuilder.php
@@ -26,7 +26,7 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N
     /** @var \Drupal\page_manager\PageVariantInterface $entity */
     $variant_plugin = $entity->getVariantPlugin();
     if ($variant_plugin instanceof ContextAwareVariantInterface) {
-      $variant_plugin->setContexts($entity->getContexts());
+      $variant_plugin->setContexts($entity->getRuntimeContexts());
     }
     if ($variant_plugin instanceof RefinableCacheableDependencyInterface) {
       $variant_plugin->addCacheableDependency($entity);
diff --git a/src/Event/PageManagerContextEvent.php b/src/Event/PageManagerContextEvent.php
deleted file mode 100644
index 5d443a4..0000000
--- a/src/Event/PageManagerContextEvent.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Event\PageManagerContextEvent.
- */
-
-namespace Drupal\page_manager\Event;
-
-use Drupal\page_manager\PageInterface;
-use Symfony\Component\EventDispatcher\Event;
-
-/**
- * Wraps a page entity for event subscribers.
- *
- * @see \Drupal\page_manager\Event\PageManagerEvents::PAGE_CONTEXT
- */
-class PageManagerContextEvent extends Event {
-
-  /**
-   * The page entity the context is gathered for.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * Creates a new PageManagerContextEvent.
-   *
-   * @param \Drupal\page_manager\PageInterface $page
-   *   The page entity.
-   */
-  public function __construct(PageInterface $page) {
-    $this->page = $page;
-  }
-
-  /**
-   * Returns the page entity for this event.
-   *
-   * @return \Drupal\page_manager\PageInterface
-   *   The page entity.
-   */
-  public function getPage() {
-    return $this->page;
-  }
-
-}
diff --git a/src/Event/PageManagerEvents.php b/src/Event/PageManagerEvents.php
deleted file mode 100644
index aa69fb8..0000000
--- a/src/Event/PageManagerEvents.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Event\PageManagerEvents.
- */
-
-namespace Drupal\page_manager\Event;
-
-/**
- * Defines events for the Page Manager module.
- */
-final class PageManagerEvents {
-
-  /**
-   * Name of the event when gathering context for a page.
-   *
-   * @see \Drupal\page_manager\Entity\Page::getContexts()
-   * @see \Drupal\page_manager\Event\PageManagerContextEvent
-   */
-  const PAGE_CONTEXT = 'page_manager.page_context';
-
-}
diff --git a/src/EventSubscriber/CurrentUserContext.php b/src/EventSubscriber/CurrentUserContext.php
deleted file mode 100644
index 7d60cfe..0000000
--- a/src/EventSubscriber/CurrentUserContext.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\EventSubscriber\CurrentUserContext.
- */
-
-namespace Drupal\page_manager\EventSubscriber;
-
-use Drupal\Core\Cache\CacheableMetadata;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Plugin\Context\ContextDefinition;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\Core\Plugin\Context\Context;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\page_manager\Event\PageManagerEvents;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Sets the current user as a context.
- */
-class CurrentUserContext implements EventSubscriberInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * The account proxy.
-   *
-   * @var \Drupal\Core\Session\AccountProxyInterface
-   */
-  protected $account;
-
-  /**
-   * The user storage.
-   *
-   * @var \Drupal\user\UserStorageInterface
-   */
-  protected $userStorage;
-
-  /**
-   * Constructs a new CurrentUserContext.
-   *
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The current account.
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity type manager.
-   */
-  public function __construct(AccountInterface $account, EntityTypeManagerInterface $entity_type_manager) {
-    $this->account = $account;
-    $this->userStorage = $entity_type_manager->getStorage('user');
-  }
-
-  /**
-   * Adds in the current user as a context.
-   *
-   * @param \Drupal\page_manager\Event\PageManagerContextEvent $event
-   *   The page entity context event.
-   */
-  public function onPageContext(PageManagerContextEvent $event) {
-    $id = $this->account->id();
-    $current_user = $this->userStorage->load($id);
-
-    $context = new Context(new ContextDefinition('entity:user', $this->t('Current user')), $current_user);
-    $cacheability = new CacheableMetadata();
-    $cacheability->setCacheContexts(['user']);
-    $context->addCacheableDependency($cacheability);
-    $event->getPage()->addContext('current_user', $context);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents() {
-    $events[PageManagerEvents::PAGE_CONTEXT][] = 'onPageContext';
-    return $events;
-  }
-
-}
diff --git a/src/ObjectWithContextAwarePluginCollectionTrait.php b/src/ObjectWithContextAwarePluginCollectionTrait.php
new file mode 100644
index 0000000..9fd4e6d
--- /dev/null
+++ b/src/ObjectWithContextAwarePluginCollectionTrait.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\ObjectWithContextAwarePluginCollectionTrait.
+ */
+
+namespace Drupal\page_manager;
+
+use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+
+/**
+ * Provides a trait for entities with context-aware plugins.
+ *
+ * @todo Move to CTools.
+ */
+trait ObjectWithContextAwarePluginCollectionTrait {
+
+  /**
+   * Finds the context mappings of all plugins contained by an entity.
+   *
+   * @param object $object
+   *   An object to inspect for plugin collections.
+   *
+   * @return string[]
+   *   An array of unqualified context IDs.
+   *
+   * @see \Drupal\Core\Plugin\Context\ContextProviderInterface::getRuntimeContexts()
+   */
+  protected function getAllContextMappingIds($object) {
+    $context_mapping_ids = [];
+    if ($object instanceof EntityWithPluginCollectionInterface || $object instanceof ObjectWithPluginCollectionInterface) {
+      foreach ($object->getPluginCollections() as $key => $collection) {
+        foreach ($collection as $plugin) {
+          if ($plugin instanceof ContextAwarePluginInterface) {
+            $context_mapping_ids = array_merge($context_mapping_ids, array_values($plugin->getContextMapping()));
+          }
+        }
+      }
+    }
+    return array_values(array_unique($context_mapping_ids));
+  }
+
+}
diff --git a/src/ObjectWithPluginCollectionInterface.php b/src/ObjectWithPluginCollectionInterface.php
new file mode 100644
index 0000000..98a1983
--- /dev/null
+++ b/src/ObjectWithPluginCollectionInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\ObjectWithPluginCollectionInterface.
+ */
+
+namespace Drupal\page_manager;
+
+/**
+ * @todo.
+ *
+ * @see \Drupal\Core\Entity\EntityWithPluginCollectionInterface
+ *
+ * @todo Move to CTools or core.
+ */
+interface ObjectWithPluginCollectionInterface {
+
+  /**
+   * Gets the plugin collections used by this entity.
+   *
+   * @return \Drupal\Component\Plugin\LazyPluginCollection[]
+   *   An array of plugin collections, keyed by the property name they use to
+   *   store their configuration.
+   */
+  public function getPluginCollections();
+
+}
diff --git a/src/PageInterface.php b/src/PageInterface.php
index 24e127f..d5a6c10 100644
--- a/src/PageInterface.php
+++ b/src/PageInterface.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\page_manager;
 
-use Drupal\Component\Plugin\Context\ContextInterface;
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
 
@@ -178,23 +177,23 @@ public function removeParameter($name);
   public function getParameterNames();
 
   /**
-   * Gets the values for all defined contexts.
+   * Gets runtime context values for all defined contexts.
    *
-   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
    *   An array of set context values, keyed by context name.
+   *
+   * @see \Drupal\Core\Plugin\Context\ContextProviderInterface::getRuntimeContexts()
    */
-  public function getContexts();
+  public function getRuntimeContexts();
 
   /**
-   * Sets the context for a given name.
+   * Gets all available contexts for the purposes of configuration.
    *
-   * @param string $name
-   *   The name of the context.
-   * @param \Drupal\Component\Plugin\Context\ContextInterface $value
-   *   The context to add.
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+   *   All available contexts.
    *
-   * @return $this
+   * @see \Drupal\Core\Plugin\Context\ContextProviderInterface:getAvailableContexts()
    */
-  public function addContext($name, ContextInterface $value);
+  public function getAvailableContexts();
 
 }
diff --git a/src/PageManagerServiceProvider.php b/src/PageManagerServiceProvider.php
new file mode 100644
index 0000000..c56b094
--- /dev/null
+++ b/src/PageManagerServiceProvider.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\PageManagerServiceProvider.
+ */
+
+namespace Drupal\page_manager;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+
+/**
+ * Adds compiler passes for Page Manager.
+ */
+class PageManagerServiceProvider implements ServiceProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) {
+    $container->addCompilerPass(new ContextProvidersPass());
+  }
+
+}
diff --git a/src/PageVariantInterface.php b/src/PageVariantInterface.php
index 12110c7..6fbf414 100644
--- a/src/PageVariantInterface.php
+++ b/src/PageVariantInterface.php
@@ -37,12 +37,20 @@ public function getVariantPluginId();
   public function getPage();
 
   /**
-   * Gets the values for all defined contexts.
+   * Gets runtime context values for all defined contexts.
    *
    * @return \Drupal\Core\Plugin\Context\ContextInterface[]
    *   An array of set context values, keyed by context name.
    */
-  public function getContexts();
+  public function getRuntimeContexts();
+
+  /**
+   * Gets all available contexts for the purposes of configuration.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+   *   All available contexts.
+   */
+  public function getAvailableContexts();
 
   /**
    * Gets the weight of this variant (compared to other variants on the page).
diff --git a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php
index 78f6dc6..0e94c2a 100644
--- a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php
+++ b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php
@@ -21,6 +21,7 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Utility\Token;
 use Drupal\ctools\Plugin\DisplayVariant\BlockDisplayVariant;
+use Drupal\page_manager\ObjectWithPluginCollectionInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -31,7 +32,7 @@
  *   admin_label = @Translation("Block page")
  * )
  */
-class PageBlockDisplayVariant extends BlockDisplayVariant {
+class PageBlockDisplayVariant extends BlockDisplayVariant implements ObjectWithPluginCollectionInterface {
 
   /**
    * The module handler.
@@ -300,4 +301,13 @@ public function getRegionNames() {
     ];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginCollections() {
+    return [
+      'blocks' => $this->getBlockCollection(),
+    ];
+  }
+
 }
diff --git a/src/Plugin/PanelsStorage/PageManagerPanelsStorage.php b/src/Plugin/PanelsStorage/PageManagerPanelsStorage.php
index 7402d5a..82b72f3 100644
--- a/src/Plugin/PanelsStorage/PageManagerPanelsStorage.php
+++ b/src/Plugin/PanelsStorage/PageManagerPanelsStorage.php
@@ -101,7 +101,7 @@ public function load($id) {
 
       // Pass down the contexts because the display has no other way to get them
       // from the variant.
-      $panels_display->setContexts($page_variant->getContexts());
+      $panels_display->setContexts($page_variant->getRuntimeContexts());
 
       return $panels_display;
     }
diff --git a/src/Routing/VariantRouteFilter.php b/src/Routing/VariantRouteFilter.php
index f588abd..6d2abcc 100644
--- a/src/Routing/VariantRouteFilter.php
+++ b/src/Routing/VariantRouteFilter.php
@@ -153,7 +153,7 @@ protected function routeWeightSort(Route $a, Route $b) {
    *   TRUE if the route is valid, FALSE otherwise.
    */
   protected function checkPageVariantAccess($page_variant_id) {
-    /** @var \Drupal\page_manager\PageVariantInterface $page */
+    /** @var \Drupal\page_manager\PageVariantInterface $variant */
     $variant = $this->pageVariantStorage->load($page_variant_id);
 
     try {
diff --git a/src/Tests/PageConfigSchemaTest.php b/src/Tests/PageConfigSchemaTest.php
index 2af9051..1b19e8c 100644
--- a/src/Tests/PageConfigSchemaTest.php
+++ b/src/Tests/PageConfigSchemaTest.php
@@ -50,7 +50,7 @@ public function testValidPageConfigSchema() {
       ],
       'negate' => TRUE,
       'context_mapping' => [
-        'node' => 'node',
+        'node' => '@page_manager.route_param_context:node',
       ],
     ]);
     $page->save();
@@ -75,7 +75,7 @@ public function testValidPageConfigSchema() {
         'page' => 'page',
       ],
       'context_mapping' => [
-        'node' => 'node',
+        'node' => '@page_manager.route_param_context:node',
       ],
     ]);
 
diff --git a/src/Tests/PageNodeAccessTest.php b/src/Tests/PageNodeAccessTest.php
index 91bf3be..df1a250 100644
--- a/src/Tests/PageNodeAccessTest.php
+++ b/src/Tests/PageNodeAccessTest.php
@@ -72,7 +72,7 @@ public function testUserRoleAccessCondition() {
         RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID,
       ],
       'context_mapping' => [
-        'user' => 'current_user',
+        'user' => '@user.current_user_context:current_user',
       ],
     ]);
     $this->page->save();
diff --git a/src/Tests/PageNodeSelectionTest.php b/src/Tests/PageNodeSelectionTest.php
index 181f57f..b89c592 100644
--- a/src/Tests/PageNodeSelectionTest.php
+++ b/src/Tests/PageNodeSelectionTest.php
@@ -94,7 +94,7 @@ public function testAdmin() {
       'view_mode' => 'default',
       'region' => 'top',
       'context_mapping' => [
-        'entity' => 'node',
+        'entity' => '@page_manager.route_param_context:node',
       ],
     ]);
     $block_page_variant->addSelectionCondition([
@@ -103,7 +103,7 @@ public function testAdmin() {
         'article' => 'article',
       ],
       'context_mapping' => [
-        'node' => 'node',
+        'node' => '@page_manager.route_param_context:node',
       ],
     ]);
     $block_page_variant->setWeight(-10);
diff --git a/src/Tests/StaticContextTest.php b/src/Tests/StaticContextTest.php
index 38ba04f..8aae7fd 100644
--- a/src/Tests/StaticContextTest.php
+++ b/src/Tests/StaticContextTest.php
@@ -110,7 +110,7 @@ public function testStaticContext() {
       'settings[label_display]' => 1,
       'settings[view_mode]' => 'default',
       'region' => 'bottom',
-      'context_mapping[entity]' => $contexts[1]['machine_name'],
+      'context_mapping[entity]' => '@page_manager.static_context_provider:static_node_2',
     ];
     $this->drupalPostForm(NULL, $edit, 'Add block');
     $this->assertText($edit['settings[label]']);
diff --git a/tests/src/Unit/CurrentUserContextTest.php b/tests/src/Unit/CurrentUserContextTest.php
deleted file mode 100644
index ec7ab04..0000000
--- a/tests/src/Unit/CurrentUserContextTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\page_manager\Unit\CurrentUserContextTest.
- */
-
-namespace Drupal\Tests\page_manager\Unit;
-
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
-use Drupal\Core\Plugin\Context\Context;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\TypedData\DataDefinition;
-use Drupal\page_manager\EventSubscriber\CurrentUserContext;
-use Drupal\user\UserInterface;
-use Prophecy\Argument;
-
-/**
- * Tests the current user context.
- *
- * @coversDefaultClass \Drupal\page_manager\EventSubscriber\CurrentUserContext
- *
- * @group PageManager
- */
-class CurrentUserContextTest extends PageContextTestBase {
-
-  /**
-   * @covers ::onPageContext
-   */
-  public function testOnPageContext() {
-    $account = $this->prophesize(AccountInterface::class);
-    $account->id()->willReturn(1);
-    $user = $this->prophesize(UserInterface::class);
-
-    $data_definition = new DataDefinition(['type' => 'entity:user']);
-
-    $this->typedDataManager->create($data_definition, $user)
-      ->willReturn(EntityAdapter::createFromEntity($user->reveal()));
-
-    $this->typedDataManager->getDefaultConstraints($data_definition)
-      ->willReturn([]);
-
-    $this->typedDataManager->createDataDefinition('entity:user')
-      ->will(function () use ($data_definition) {
-        return $data_definition;
-      });
-
-    $this->page->addContext('current_user', Argument::type(Context::class))->shouldBeCalled();
-
-    $user_storage = $this->prophesize(EntityStorageInterface::class);
-    $user_storage->load(1)->willReturn($user->reveal());
-
-    $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
-    $entity_type_manager->getStorage('user')->willReturn($user_storage->reveal());
-
-    $route_param_context = new CurrentUserContext($account->reveal(), $entity_type_manager->reveal());
-    $route_param_context->onPageContext($this->event);
-  }
-
-}
diff --git a/tests/src/Unit/ObjectWithContextAwarePluginCollectionTraitTest.php b/tests/src/Unit/ObjectWithContextAwarePluginCollectionTraitTest.php
new file mode 100644
index 0000000..50c5f52
--- /dev/null
+++ b/tests/src/Unit/ObjectWithContextAwarePluginCollectionTraitTest.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains
+ *   \Drupal\Tests\page_manager\Unit\ObjectWithContextAwarePluginCollectionTraitTest.
+ */
+
+namespace Drupal\Tests\page_manager\Unit;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+use Drupal\Core\Plugin\DefaultLazyPluginCollection;
+use Drupal\page_manager\ObjectWithContextAwarePluginCollectionTrait;
+use Drupal\page_manager\ObjectWithPluginCollectionInterface;
+use Drupal\Tests\Core\Config\Entity\Fixtures\ConfigEntityBaseWithPluginCollections;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\page_manager\ObjectWithContextAwarePluginCollectionTrait
+ *
+ * @group PageManager
+ */
+class ObjectWithContextAwarePluginCollectionTraitTest extends UnitTestCase{
+
+  /**
+   * @covers ::getAllContextMappingIds
+   * @dataProvider providerTestGetAllContextMappingIds
+   */
+  public function testGetAllContextMappingIds($object) {
+    $plugin1 = $this->prophesize(ContextAwarePluginInterface::class);
+    $plugin1->getContextMapping()->willReturn(['foo' => 'John']);
+    $plugin2 = $this->prophesize(ContextAwarePluginInterface::class);
+    $plugin2->getContextMapping()->willReturn(['foo' => 'John', 'bar' => 'Paul']);
+    $plugin3 = $this->prophesize(ContextAwarePluginInterface::class);
+    $plugin3->getContextMapping()->willReturn(['foo' => 'George', 'bar' => 'Ringo']);
+    $plugin4 = $this->prophesize(ContextAwarePluginInterface::class);
+    $plugin4->getContextMapping()->willReturn(['foo' => 'Ringo', 'bar' => 'George']);
+
+    $plugin_manager = $this->prophesize(PluginManagerInterface::class);
+    $plugin_manager->createInstance('plugin_id1', ['id' => 'plugin_id1'])->willReturn($plugin1->reveal());
+    $plugin_manager->createInstance('plugin_id2', ['id' => 'plugin_id2'])->willReturn($plugin2->reveal());
+    $plugin_manager->createInstance('plugin_id3', ['id' => 'plugin_id3'])->willReturn($plugin3->reveal());
+    $plugin_manager->createInstance('plugin_id4', ['id' => 'plugin_id4'])->willReturn($plugin4->reveal());
+
+    $object->setPluginManager($plugin_manager->reveal());
+
+    $expected = ['John', 'Paul', 'George', 'Ringo'];
+    $context_mapping_ids = $object->testGetAllContextMappingIds();
+    $this->assertSame($expected, $context_mapping_ids);
+  }
+
+  public function providerTestGetAllContextMappingIds() {
+    $data = [];
+    $data['entity'] = [new TestEntity([], '')];
+    $data['object'] = [new TestObject()];
+    return $data;
+  }
+
+}
+
+class TestObject implements ObjectWithPluginCollectionInterface {
+
+  use ObjectWithContextAwarePluginCollectionTrait;
+  use HelperTrait;
+
+}
+
+class TestEntity extends ConfigEntityBaseWithPluginCollections {
+
+  use ObjectWithContextAwarePluginCollectionTrait;
+  use HelperTrait;
+
+}
+
+trait HelperTrait {
+
+  protected $pluginManager;
+
+  public function testGetAllContextMappingIds() {
+    return $this->getAllContextMappingIds($this);
+  }
+
+  public function setPluginManager(PluginManagerInterface $plugin_manager) {
+    $this->pluginManager = $plugin_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginCollections() {
+    return [
+      'plugin_collection1' => new DefaultLazyPluginCollection($this->pluginManager, [
+        'plugin_id1' => ['id' => 'plugin_id1'],
+        'plugin_id2' => ['id' => 'plugin_id2'],
+      ]),
+      'plugin_collection2' => new DefaultLazyPluginCollection($this->pluginManager, [
+        'plugin_id3' => ['id' => 'plugin_id3'],
+        'plugin_id4' => ['id' => 'plugin_id4'],
+      ]),
+    ];
+  }
+
+}
diff --git a/tests/src/Unit/PageAccessTest.php b/tests/src/Unit/PageAccessTest.php
index f3ce5ed..b58595d 100644
--- a/tests/src/Unit/PageAccessTest.php
+++ b/tests/src/Unit/PageAccessTest.php
@@ -76,7 +76,7 @@ public function setUp() {
   public function testAccessView() {
     $page = $this->prophesize(PageInterface::class);
 
-    $page->getContexts()->willReturn([]);
+    $page->getRuntimeContexts()->willReturn([]);
     $page->getAccessConditions()->willReturn([]);
     $page->getAccessLogic()->willReturn('and');
     $page->status()->willReturn(TRUE);
diff --git a/tests/src/Unit/PageContextTestBase.php b/tests/src/Unit/PageContextTestBase.php
deleted file mode 100644
index 4172675..0000000
--- a/tests/src/Unit/PageContextTestBase.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\page_manager\Unit\PageContextTestBase.
- */
-
-namespace Drupal\Tests\page_manager\Unit;
-
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\TypedData\TypedDataManager;
-use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\page_manager\PageInterface;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * @todo.
- */
-abstract class PageContextTestBase extends UnitTestCase {
-
-  /**
-   * The typed data manager.
-   *
-   * @var \Drupal\Core\TypedData\TypedDataManager|\Prophecy\Prophecy\ProphecyInterface
-   */
-  protected $typedDataManager;
-
-  /**
-   * The page entity.
-   *
-   * @var \Drupal\page_manager\PageInterface|\Prophecy\Prophecy\ProphecyInterface
-   */
-  protected $page;
-
-  /**
-   * The event.
-   *
-   * @var \Drupal\page_manager\Event\PageManagerContextEvent
-   */
-  protected $event;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp() {
-    parent::setUp();
-
-    $this->typedDataManager = $this->prophesize(TypedDataManager::class);
-
-    $container = new ContainerBuilder();
-    $container->set('string_translation', $this->getStringTranslationStub());
-    $container->set('typed_data_manager', $this->typedDataManager->reveal());
-    \Drupal::setContainer($container);
-
-    $this->page = $this->prophesize(PageInterface::class);
-
-    $this->event = new PageManagerContextEvent($this->page->reveal());
-  }
-
-}
diff --git a/tests/src/Unit/PageTest.php b/tests/src/Unit/PageTest.php
index 84b40aa..5589d94 100644
--- a/tests/src/Unit/PageTest.php
+++ b/tests/src/Unit/PageTest.php
@@ -7,18 +7,17 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
+use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\page_manager\Context\PageContextRepositoryInterface;
 use Drupal\page_manager\Entity\Page;
-use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\page_manager\Event\PageManagerEvents;
 use Drupal\page_manager\PageVariantInterface;
 use Drupal\Tests\UnitTestCase;
 use Prophecy\Argument;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Tests the Page entity.
@@ -74,32 +73,22 @@ public function testGetVariants() {
   }
 
   /**
-   * @covers ::addContext
+   * @covers ::getRuntimeContexts
    */
-  public function testAddContext() {
+  public function testGetRuntimeContexts() {
     $context = new Context(new ContextDefinition('bar'));
-    $this->page->addContext('foo', $context);
-    $contexts = $this->page->getContexts();
-    $this->assertSame(['foo' => $context], $contexts);
-  }
 
-  /**
-   * @covers ::getContexts
-   */
-  public function testGetContexts() {
-    $context = new Context(new ContextDefinition('bar'));
+    $context_repository = $this->prophesize(PageContextRepositoryInterface::class);
+    $context_repository->getRuntimePageOnlyContexts([], $this->page)->willReturn(['foo' => $context]);
 
-    $event_dispatcher = $this->prophesize(EventDispatcherInterface::class);
-    $event_dispatcher->dispatch(PageManagerEvents::PAGE_CONTEXT, Argument::type(PageManagerContextEvent::class))
-      ->will(function ($args) use ($context) {
-        $args[1]->getPage()->addContext('foo', $context);
-      });
+    $condition_plugin_manager = $this->prophesize(PluginManagerInterface::class);
 
     $container = new ContainerBuilder();
-    $container->set('event_dispatcher', $event_dispatcher->reveal());
+    $container->set('page_manager.context_repository', $context_repository->reveal());
+    $container->set('plugin.manager.condition', $condition_plugin_manager->reveal());
     \Drupal::setContainer($container);
 
-    $contexts = $this->page->getContexts();
+    $contexts = $this->page->getRuntimeContexts();
     $this->assertSame(['foo' => $context], $contexts);
   }
 
diff --git a/tests/src/Unit/PageVariantTest.php b/tests/src/Unit/PageVariantTest.php
index cfe8ead..43d177e 100644
--- a/tests/src/Unit/PageVariantTest.php
+++ b/tests/src/Unit/PageVariantTest.php
@@ -7,12 +7,10 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
+use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\page_manager\ContextMapperInterface;
+use Drupal\page_manager\Context\PageContextRepositoryInterface;
 use Drupal\page_manager\Entity\PageVariant;
-use Drupal\page_manager\PageInterface;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -33,9 +31,9 @@ class PageVariantTest extends UnitTestCase {
   protected $page;
 
   /**
-   * @var \Drupal\page_manager\ContextMapperInterface|\Prophecy\Prophecy\ProphecyInterface
+   * @var \Drupal\page_manager\Context\PageContextRepositoryInterface|\Prophecy\Prophecy\ProphecyInterface
    */
-  protected $contextMapper;
+  protected $contextRepository;
 
   /**
    * {@inheritdoc}
@@ -44,37 +42,36 @@ protected function setUp() {
     parent::setUp();
 
     $this->pageVariant = new PageVariant(['id' => 'the_page_variant', 'page' => 'the_page'], 'page_variant');
-    $this->page = $this->prophesize(PageInterface::class);
 
-    $entity_storage = $this->prophesize(EntityStorageInterface::class);
-    $entity_storage->load('the_page')->willReturn($this->page->reveal());
+    $this->contextRepository = $this->prophesize(PageContextRepositoryInterface::class);
 
-    $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
-    $entity_type_manager->getStorage('page')->willReturn($entity_storage);
-
-    $this->contextMapper = $this->prophesize(ContextMapperInterface::class);
+    $condition_plugin_manager = $this->prophesize(PluginManagerInterface::class);
+    $display_variant_plugin_manager = $this->prophesize(PluginManagerInterface::class);
 
     $container = new ContainerBuilder();
-    $container->set('entity_type.manager', $entity_type_manager->reveal());
-    $container->set('page_manager.context_mapper', $this->contextMapper->reveal());
+    $container->set('plugin.manager.condition', $condition_plugin_manager->reveal());
+    $container->set('plugin.manager.display_variant', $display_variant_plugin_manager->reveal());
+    $container->set('page_manager.context_repository', $this->contextRepository->reveal());
     \Drupal::setContainer($container);
   }
 
   /**
-   * @covers ::getContexts
-   * @dataProvider providerTestGetContexts
+   * @covers ::getRuntimeContexts
+   * @dataProvider providerTestGetRuntimeContexts
    */
-  public function testGetContexts($static_contexts, $page_contexts, $expected) {
-    $this->contextMapper->getContextValues([])->willReturn($static_contexts)->shouldBeCalledTimes(1);
-    $this->page->getContexts()->willReturn($page_contexts)->shouldBeCalledTimes(1);
+  public function testGetRuntimeContexts($static_contexts, $page_contexts, $expected) {
+    $this->contextRepository
+      ->getRuntimeContexts([], $this->pageVariant)
+      ->willReturn(array_merge($static_contexts, $page_contexts))
+      ->shouldBeCalledTimes(1);
 
-    $contexts = $this->pageVariant->getContexts();
+    $contexts = $this->pageVariant->getRuntimeContexts();
     $this->assertSame($expected, $contexts);
-    $contexts = $this->pageVariant->getContexts();
+    $contexts = $this->pageVariant->getRuntimeContexts();
     $this->assertSame($expected, $contexts);
   }
 
-  public function providerTestGetContexts() {
+  public function providerTestGetRuntimeContexts() {
     $data = [];
     $data['empty'] = [
       [],
@@ -95,18 +92,17 @@ public function providerTestGetContexts() {
   }
 
   /**
-   * @covers ::getContexts
+   * @covers ::getRuntimeContexts
    * @covers ::removeStaticContext
    */
-  public function testGetContextsAfterReset() {
-    $this->contextMapper->getContextValues([])->willReturn([])->shouldBeCalledTimes(2);
-    $this->page->getContexts()->willReturn([])->shouldBeCalledTimes(2);
+  public function testGetRuntimeContextsAfterReset() {
+    $this->contextRepository->getRuntimeContexts([], $this->pageVariant)->willReturn([])->shouldBeCalledTimes(2);
 
     $expected = [];
-    $contexts = $this->pageVariant->getContexts();
+    $contexts = $this->pageVariant->getRuntimeContexts();
     $this->assertSame($expected, $contexts);
     $this->pageVariant->removeStaticContext('anything');
-    $contexts = $this->pageVariant->getContexts();
+    $contexts = $this->pageVariant->getRuntimeContexts();
     $this->assertSame($expected, $contexts);
   }
 
diff --git a/tests/src/Unit/PanelsStorageTest.php b/tests/src/Unit/PanelsStorageTest.php
index 4ee011f..29e6e3f 100644
--- a/tests/src/Unit/PanelsStorageTest.php
+++ b/tests/src/Unit/PanelsStorageTest.php
@@ -64,7 +64,7 @@ protected function setUp() {
     $this->pageVariant->getVariantPlugin()->willReturn($this->panelsDisplay->reveal());
 
     $this->pageVariantNotPanels = $this->prophesize(PageVariantInterface::class);
-    $this->pageVariantNotPanels->getContexts()->shouldNotBeCalled();
+    $this->pageVariantNotPanels->getRuntimeContexts()->shouldNotBeCalled();
 
     $non_panels_variant = $this->prophesize(HttpStatusCodeDisplayVariant::class);
     $this->pageVariantNotPanels->getVariantPlugin()->willReturn($non_panels_variant->reveal());
@@ -80,7 +80,7 @@ protected function setUp() {
    */
   public function testLoad() {
     // Make sure that the contexts are passed down (or not).
-    $this->pageVariant->getContexts()->willReturn([]);
+    $this->pageVariant->getRuntimeContexts()->willReturn([]);
     $this->panelsDisplay->setContexts([])->shouldBeCalledTimes(1);
 
     $this->storage->load('id_exists')->willReturn($this->pageVariant->reveal());
diff --git a/tests/src/Unit/RouteParamContextTest.php b/tests/src/Unit/RouteParamContextTest.php
index 0115d68..ef5334b 100644
--- a/tests/src/Unit/RouteParamContextTest.php
+++ b/tests/src/Unit/RouteParamContextTest.php
@@ -7,11 +7,15 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\TypedDataInterface;
-use Drupal\page_manager\EventSubscriber\RouteParamContext;
+use Drupal\Core\TypedData\TypedDataManager;
+use Drupal\page_manager\ContextProvider\RouteParamContext;
+use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
+use Drupal\Tests\UnitTestCase;
 use Prophecy\Argument;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -21,16 +25,50 @@
 /**
  * Tests the route param context.
  *
- * @coversDefaultClass \Drupal\page_manager\EventSubscriber\RouteParamContext
+ * @coversDefaultClass \Drupal\page_manager\ContextProvider\RouteParamContext
  *
  * @group PageManager
  */
-class RouteParamContextTest extends PageContextTestBase {
+class RouteParamContextTest extends UnitTestCase {
 
   /**
-   * @covers ::onPageContext
+   * The typed data manager.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager|\Prophecy\Prophecy\ProphecyInterface
    */
-  public function testOnPageContext() {
+  protected $typedDataManager;
+
+  /**
+   * The page entity.
+   *
+   * @var \Drupal\page_manager\PageInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $page;
+
+  protected $pageVariant;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->typedDataManager = $this->prophesize(TypedDataManager::class);
+
+    $container = new ContainerBuilder();
+    $container->set('string_translation', $this->getStringTranslationStub());
+    $container->set('typed_data_manager', $this->typedDataManager->reveal());
+    \Drupal::setContainer($container);
+
+    $this->page = $this->prophesize(PageInterface::class);
+    $this->pageVariant = $this->prophesize(PageVariantInterface::class);
+    $this->pageVariant->getPage()->willReturn($this->page->reveal());
+  }
+
+  /**
+   * @covers ::getRuntimeContexts
+   */
+  public function testGetRuntimeContexts() {
     $collection = new RouteCollection();
     $route_provider = $this->prophesize(RouteProviderInterface::class);
     $route_provider->getRoutesByPattern('/test_route')->willReturn($collection);
@@ -48,10 +86,9 @@ public function testOnPageContext() {
     $this->typedDataManager->create($data_definition, 'banana')
       ->willReturn($typed_data->reveal());
 
-    $this->typedDataManager->createDataDefinition('bar')
-      ->will(function () use ($data_definition) {
-        return $data_definition;
-      });
+    $this->typedDataManager->createDataDefinition('bar')->will(function () use ($data_definition) {
+      return $data_definition;
+    });
 
     $this->page->getPath()->willReturn('/test_route');
 
@@ -59,16 +96,6 @@ public function testOnPageContext() {
     $this->page->getParameter('baz')->willReturn(['machine_name' => 'baz', 'type' => 'integer', 'label' => '']);
     $this->page->getParameter('page')->willReturn(['machine_name' => 'page', 'type' => 'entity:page', 'label' => '']);
 
-    $this->page->addContext('foo', Argument::that(function ($context) {
-      return $context instanceof Context && $context->getContextDefinition()->getLabel() == 'Foo';
-    }))->shouldBeCalled();
-    $this->page->addContext('baz', Argument::that(function ($context) {
-      return $context instanceof Context && $context->getContextDefinition()->getLabel() == '{baz} from route';
-    }))->shouldBeCalled();
-    $this->page->addContext('page', Argument::that(function ($context) {
-      return $context instanceof Context && $context->getContextDefinition()->getLabel() == '{page} from route';
-    }))->shouldBeCalled();
-
     $collection->add('test_route', new Route('/test_route', [], [], [
       'parameters' => [
         'foo' => ['type' => 'bar'],
@@ -81,13 +108,21 @@ public function testOnPageContext() {
     $request->attributes->add(['foo' => 'banana']);
 
     $route_param_context = new RouteParamContext($route_provider->reveal(), $request_stack);
-    $route_param_context->onPageContext($this->event);
+    $contexts = $route_param_context->getRuntimeContexts([], $this->pageVariant->reveal());
+    $this->assertSame(['foo', 'baz', 'page'], array_keys($contexts));
+    $this->assertSame('Foo', (string) $contexts['foo']->getContextDefinition()->getLabel());
+    $this->assertSame('{baz} from route', (string) $contexts['baz']->getContextDefinition()->getLabel());
+    $this->assertSame('{page} from route', (string) $contexts['page']->getContextDefinition()->getLabel());
+
+    $this->assertSame('bar', $contexts['foo']->getContextDefinition()->getDataType());
+    $this->assertSame('bop', $contexts['baz']->getContextDefinition()->getDataType());
+    $this->assertSame('entity:page', $contexts['page']->getContextDefinition()->getDataType());
   }
 
   /**
-   * @covers ::onPageContext
+   * @covers ::getRuntimeContexts
    */
-  public function testOnPageContextEmpty() {
+  public function testGetRuntimeContextsEmpty() {
     $collection = new RouteCollection();
     $route_provider = $this->prophesize(RouteProviderInterface::class);
     $route_provider->getRoutesByPattern('/test_route')->willReturn($collection);
@@ -98,13 +133,12 @@ public function testOnPageContextEmpty() {
 
     $this->page->getPath()->willReturn('/test_route');
 
-    $this->page->addContext(Argument::cetera())->shouldNotBeCalled();
-
     // Set up a request with one of the expected parameters as an attribute.
     $request->attributes->add(['foo' => 'banana']);
 
     $route_param_context = new RouteParamContext($route_provider->reveal(), $request_stack);
-    $route_param_context->onPageContext($this->event);
+    $contexts = $route_param_context->getRuntimeContexts([], $this->pageVariant->reveal());
+    $this->assertEmpty($contexts);
   }
 
 }
