diff --git a/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php b/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php
index 0cb7c68..216d491 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php
@@ -2,38 +2,68 @@
 
 namespace Drupal\migrate\Plugin\migrate\process;
 
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Menu\MenuLinkManagerInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Url;
+use Drupal\migrate\Plugin\MigratePluginManagerInterface;
 use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Plugin\migrate\process\Migration as Lookup;
 use Drupal\migrate\MigrateExecutableInterface;
 use Drupal\migrate\MigrateSkipRowException;
-use Drupal\migrate\Plugin\MigrateProcessInterface;
-use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
 use Drupal\migrate\Row;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * This plugin figures out menu link parent plugin IDs.
+ * Determines the parent of a menu link.
+ *
+ * Available configuration keys:
+ * - 'skip_row': If set, a MigrateSkipRowException is thrown if the parent
+ *   cannot be ascertained. Defaults to TRUE.
+ *
+ * The source is an array of three values:
+ * - parent_id: The numeric ID of the parent menu link, or 0 if the link is at
+ *   the top level of its menu.
+ * - menu_name: The menu name. All links with the same menu name (such as
+ *   'navigation') are part of the same menu.
+ * - parent_link_path: The Drupal path or external URL this link points to.
+ *
+ * Example:
+ *
+ * @code
+ * process:
+ *   parent:
+ *     plugin: menu_link_parent
+ *     source:
+ *       - '20'
+ *       - 'admin'
+ *       - 'admin/structure'
+ * @endcode
+ *
+ * In this example the parent_id of '20' will be looked up and returned from
+ * amongst the already migrated IDs. If it is not found then the
+ * parent_link_path of 'admin/structure' is loaded from the 'admin' menu, the
+ * given menu_name.
+ *
+ * @see \Drupal\migrate\Plugin\MigrateProcessInterface
  *
  * @MigrateProcessPlugin(
  *   id = "menu_link_parent"
  * )
  */
-class MenuLinkParent extends ProcessPluginBase implements ContainerFactoryPluginInterface {
+class MenuLinkParent extends Lookup {
 
   /**
+   * The menu link plugin manager.
+   *
    * @var \Drupal\Core\Menu\MenuLinkManagerInterface
    */
   protected $menuLinkManager;
 
   /**
-   * @var \Drupal\migrate\Plugin\MigrateProcessInterface
-   */
-  protected $migrationPlugin;
-
-  /**
+   * The menu link entity storage handler.
+   *
    * @var \Drupal\Core\Entity\EntityStorageInterface
    */
   protected $menuLinkStorage;
@@ -41,9 +71,21 @@ class MenuLinkParent extends ProcessPluginBase implements ContainerFactoryPlugin
   /**
    * {@inheritdoc}
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrateProcessInterface $migration_plugin, MenuLinkManagerInterface $menu_link_manager, EntityStorageInterface $menu_link_storage) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->migrationPlugin = $migration_plugin;
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManagerInterface $process_plugin_manager, MenuLinkManagerInterface $menu_link_manager, EntityStorageInterface $menu_link_storage) {
+    $configuration += [
+      // Skip the row by default if lookup fails.
+      'skip_row' => TRUE,
+      // Find parent menu link IDs already migrated by the current migration.
+      'migration' => $migration->id(),
+    ];
+    parent::__construct(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $migration_plugin_manager,
+      $process_plugin_manager
+    );
     $this->menuLinkManager = $menu_link_manager;
     $this->menuLinkStorage = $menu_link_storage;
   }
@@ -52,52 +94,79 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
-    $migration_configuration['migration'][] = $migration->id();
     return new static(
       $configuration,
       $plugin_id,
       $plugin_definition,
-      $container->get('plugin.manager.migrate.process')->createInstance('migration', $migration_configuration, $migration),
+      $migration,
+      $container->get('plugin.manager.migration'),
+      $container->get('plugin.manager.migrate.process'),
       $container->get('plugin.manager.menu.link'),
-      $container->get('entity.manager')->getStorage('menu_link_content')
+      $container->get('entity_type.manager')->getStorage('menu_link_content')
     );
   }
 
   /**
    * {@inheritdoc}
-   *
-   * Find the parent link GUID.
    */
   public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
     $parent_id = array_shift($value);
+
+    // If the parent ID is empty, it's a top-level menu item.
     if (!$parent_id) {
-      // Top level item.
       return '';
     }
+
+    // Try to look up the parent menu link ID from among the ones that have
+    // already been migrated.
     try {
-      $already_migrated_id = $this
-        ->migrationPlugin
-        ->transform($parent_id, $migrate_executable, $row, $destination_property);
-      if ($already_migrated_id && ($link = $this->menuLinkStorage->load($already_migrated_id))) {
-        return $link->getPluginId();
-      }
+      $parent_id = parent::transform($parent_id, $migrate_executable, $row, $destination_property);
     }
     catch (MigrateSkipRowException $e) {
-
+      // There are two reasons the exception may have been thrown. Either the
+      // input value was empty -- definitely not the case, since we checked for
+      // that at the top of this method -- or the lookup tried to stub the
+      // parent menu link, but the destination's configuration has no_stub set
+      // to TRUE. In this latter case, we'll just act as if the lookup failed,
+      // and fall back to looking for the parent link using the menu name and
+      // parent link path.
+      //
+      // TODO: Introduce a new exception to be thrown if stubbing fails or is
+      // disallowed, and catch that instead.
+      $parent_id = NULL;
+    }
+    if ($parent_id) {
+      return $this->menuLinkStorage->load($parent_id)->getPluginId();
     }
-    if (isset($value[1])) {
-      list($menu_name, $parent_link_path) = $value;
-      $url = Url::fromUserInput("/$parent_link_path");
-      if ($url->isRouted()) {
-        $links = $this->menuLinkManager->loadLinksByRoute($url->getRouteName(), $url->getRouteParameters(), $menu_name);
-        if (count($links) == 1) {
-          /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
-          $link = reset($links);
-          return $link->getPluginId();
+
+    if ($value) {
+      list ($menu_name, $parent_link_path) = $value;
+
+      // If the parent link path is external, Url will be useless because the
+      // link will definitely not correspond to a Drupal route.
+      if (UrlHelper::isExternal($parent_link_path)) {
+        $links = $this->menuLinkStorage->loadByProperties([
+          'menu_name' => $menu_name,
+          'link.uri' => $parent_link_path,
+        ]);
+      }
+      else {
+        $url = Url::fromUserInput('/' . ltrim($parent_link_path, '/'));
+        if ($url->isRouted()) {
+          $links = $this->menuLinkManager->loadLinksByRoute($url->getRouteName(), $url->getRouteParameters(), $menu_name);
         }
       }
+
+      if (!empty($links)) {
+        return reset($links)->getPluginId();
+      }
+    }
+
+    // TODO: Remove this behavior and the skip_row configuration option.
+    if (!empty($this->configuration['skip_row'])) {
+      throw new MigrateSkipRowException();
     }
-    throw new MigrateSkipRowException();
+    return NULL;
   }
 
 }
