diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/UriWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/UriWidget.php
new file mode 100644
index 0000000000000000000000000000000000000000..39606c6cc90db34b85c0a5f11e7a127b93d0bf47
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/UriWidget.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\UriWidget.
+ */
+
+namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\WidgetBase;
+
+/**
+ * Plugin implementation of the 'uri' widget.
+ *
+ * @FieldWidget(
+ *   id = "uri",
+ *   label = @Translation("URI field"),
+ *   field_types = {
+ *     "uri",
+ *   }
+ * )
+ */
+class UriWidget extends WidgetBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    return array(
+      'size' => 60,
+      'placeholder' => '',
+    ) + parent::defaultSettings();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    $element['size'] = array(
+      '#type' => 'number',
+      '#title' => $this->t('Size of URI field'),
+      '#default_value' => $this->getSetting('size'),
+      '#required' => TRUE,
+      '#min' => 1,
+    );
+    $element['placeholder'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Placeholder'),
+      '#default_value' => $this->getSetting('placeholder'),
+      '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
+    );
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+    $summary = array();
+
+    $summary[] = $this->t('URI field size: !size', array('!size' => $this->getSetting('size')));
+    $placeholder = $this->getSetting('placeholder');
+    if (!empty($placeholder)) {
+      $summary[] = $this->t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
+    $element['value'] = $element + array(
+      '#type' => 'url',
+      '#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
+      '#size' => $this->getSetting('size'),
+      '#placeholder' => $this->getSetting('placeholder'),
+      '#maxlength' => $this->getFieldSetting('max_length'),
+    );
+    return $element;
+  }
+
+}
diff --git a/core/modules/aggregator/aggregator.info.yml b/core/modules/aggregator/aggregator.info.yml
index 1a690eeb0ffd64307599ec3adc1374808ba41811..af55280a6e010ce4e3e8f67892ccd9fd92b65711 100644
--- a/core/modules/aggregator/aggregator.info.yml
+++ b/core/modules/aggregator/aggregator.info.yml
@@ -7,3 +7,4 @@ core: 8.x
 configure: aggregator.admin_settings
 dependencies:
   - file
+  - options
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
index ff93c30d72fc6fbef1d48cbcccb3d53c294e7c1b..daa3fba7bdac6f6c9214c1ccf60779daeab98fea 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
@@ -144,7 +144,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['title'] = FieldDefinition::create('string')
       ->setLabel(t('Title'))
-      ->setDescription(t('The title of the feed.'));
+      ->setDescription(t('The name of the feed (or the name of the website providing the feed).'))
+      ->setRequired(TRUE)
+      ->setSetting('max_length', 255)
+      ->setDisplayOptions('form', array(
+        'type' => 'string',
+        'weight' => -5,
+      ));
 
     $fields['langcode'] = FieldDefinition::create('language')
       ->setLabel(t('Language code'))
@@ -152,12 +158,28 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['url'] = FieldDefinition::create('uri')
       ->setLabel(t('URL'))
-      ->setDescription(t('The URL to the feed.'));
-
-    $fields['refresh'] = FieldDefinition::create('integer')
-      ->setLabel(t('Refresh'))
-      ->setDescription(t('How often to check for new feed items, in seconds.'))
-      ->setSetting('unsigned', TRUE);
+      ->setDescription(t('The fully-qualified URL of the feed.'))
+      ->setRequired(TRUE)
+      ->setSetting('max_length', NULL)
+      ->setDisplayOptions('form', array(
+        'type' => 'uri',
+        'weight' => -3,
+      ));
+
+    $intervals = array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200);
+    $period = array_map('format_interval', array_combine($intervals, $intervals));
+    $period[AGGREGATOR_CLEAR_NEVER] = t('Never');
+
+    $fields['refresh'] = FieldDefinition::create('list_integer')
+      ->setLabel(t('Update interval'))
+      ->setDescription(t('The length of time between feed updates. Requires a correctly configured <a href="@cron">cron maintenance task</a>.', array('@cron' => url('admin/reports/status'))))
+      ->setSetting('unsigned', TRUE)
+      ->setRequired(TRUE)
+      ->setSetting('allowed_values', $period)
+      ->setDisplayOptions('form', array(
+        'type' => 'options_select',
+        'weight' => -2,
+      ));
 
     $fields['checked'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Checked'))
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php
index e80a1d3b66c04144a247394a92ebd154a3719f2e..51fdc8f33d02db37ebe79a49a31b6875ce2fc93b 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php
@@ -23,39 +23,16 @@ class FeedFormController extends ContentEntityFormController {
    */
   public function form(array $form, array &$form_state) {
     $feed = $this->entity;
-    $intervals = array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200);
-    $period = array_map('format_interval', array_combine($intervals, $intervals));
-    $period[AGGREGATOR_CLEAR_NEVER] = $this->t('Never');
-
-    $form['title'] = array(
-      '#type' => 'textfield',
-      '#title' => $this->t('Title'),
-      '#default_value' => $feed->label(),
-      '#maxlength' => 255,
-      '#description' => $this->t('The name of the feed (or the name of the website providing the feed).'),
-      '#required' => TRUE,
-    );
 
+    // @todo: convert to a language selection widget defined in the base field.
+    //   Blocked on https://drupal.org/node/2226493 which adds a generic
+    //   language widget.
     $form['langcode'] = array(
       '#title' => $this->t('Language'),
       '#type' => 'language_select',
       '#default_value' => $feed->language()->id,
       '#languages' => Language::STATE_ALL,
-    );
-
-    $form['url'] = array(
-      '#type' => 'url',
-      '#title' => $this->t('URL'),
-      '#default_value' => $feed->getUrl(),
-      '#maxlength' => NULL,
-      '#description' => $this->t('The fully-qualified URL of the feed.'),
-      '#required' => TRUE,
-    );
-    $form['refresh'] = array('#type' => 'select',
-      '#title' => $this->t('Update interval'),
-      '#default_value' => $feed->getRefreshRate(),
-      '#options' => $period,
-      '#description' => $this->t('The length of time between feed updates. Requires a correctly configured <a href="@cron">cron maintenance task</a>.', array('@cron' => url('admin/reports/status'))),
+      '#weight' => -4,
     );
 
     return parent::form($form, $form_state, $feed);
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php
index e900025f5222e05d3bb9b687997edf19669eef4a..6a85657bc46f083c3e4c7d2bed7fc4a36d8c7cd1 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php
@@ -54,9 +54,9 @@ function setUp() {
   function createFeed($feed_url = NULL, array $edit = array()) {
     $edit = $this->getFeedEditArray($feed_url, $edit);
     $this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
-    $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), format_string('The feed !name has been added.', array('!name' => $edit['title'])));
+    $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title[0][value]'])), format_string('The feed !name has been added.', array('!name' => $edit['title[0][value]'])));
 
-    $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title'], ':url' => $edit['url']))->fetchField();
+    $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title[0][value]'], ':url' => $edit['url[0][value]']))->fetchField();
     $this->assertTrue(!empty($fid), 'The feed found in database.');
     return aggregator_feed_load($fid);
   }
@@ -93,8 +93,8 @@ function getFeedEditArray($feed_url = NULL, array $edit = array()) {
       ));
     }
     $edit += array(
-      'title' => $feed_name,
-      'url' => $feed_url,
+      'title[0][value]' => $feed_name,
+      'url[0][value]' => $feed_url,
       'refresh' => '900',
     );
     return $edit;
@@ -233,7 +233,7 @@ function uniqueFeed($feed_name, $feed_url) {
   function getValidOpml($feeds) {
     // Properly escape URLs so that XML parsers don't choke on them.
     foreach ($feeds as &$feed) {
-      $feed['url'] = htmlspecialchars($feed['url']);
+      $feed['url[0][value]'] = htmlspecialchars($feed['url[0][value]']);
     }
     /**
      * Does not have an XML declaration, must pass the parser.
@@ -243,18 +243,18 @@ function getValidOpml($feeds) {
   <head></head>
   <body>
     <!-- First feed to be imported. -->
-    <outline text="{$feeds[0]['title']}" xmlurl="{$feeds[0]['url']}" />
+    <outline text="{$feeds[0]['title[0][value]']}" xmlurl="{$feeds[0]['url[0][value]']}" />
 
     <!-- Second feed. Test string delimitation and attribute order. -->
-    <outline xmlurl='{$feeds[1]['url']}' text='{$feeds[1]['title']}'/>
+    <outline xmlurl='{$feeds[1]['url[0][value]']}' text='{$feeds[1]['title[0][value]']}'/>
 
     <!-- Test for duplicate URL and title. -->
-    <outline xmlurl="{$feeds[0]['url']}" text="Duplicate URL"/>
-    <outline xmlurl="http://duplicate.title" text="{$feeds[1]['title']}"/>
+    <outline xmlurl="{$feeds[0]['url[0][value]']}" text="Duplicate URL"/>
+    <outline xmlurl="http://duplicate.title" text="{$feeds[1]['title[0][value]']}"/>
 
     <!-- Test that feeds are only added with required attributes. -->
-    <outline text="{$feeds[2]['title']}" />
-    <outline xmlurl="{$feeds[2]['url']}" />
+    <outline text="{$feeds[2]['title[0][value]']}" />
+    <outline xmlurl="{$feeds[2]['url[0][value]']}" />
   </body>
 </opml>
 EOF;
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php
index ffdc6ce7707904388b4f7b79a296b4c6f621559b..e826d8131bf53bb8ca66cbc32565626b8f422030 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php
@@ -101,8 +101,8 @@ function submitImportForm() {
       'refresh'       => '900',
     );
     $this->drupalPostForm('admin/config/services/aggregator/add/opml', $edit, t('Import'));
-    $this->assertRaw(t('A feed with the URL %url already exists.', array('%url' => $feeds[0]['url'])), 'Verifying that a duplicate URL was identified');
-    $this->assertRaw(t('A feed named %title already exists.', array('%title' => $feeds[1]['title'])), 'Verifying that a duplicate title was identified');
+    $this->assertRaw(t('A feed with the URL %url already exists.', array('%url' => $feeds[0]['url[0][value]'])), 'Verifying that a duplicate URL was identified');
+    $this->assertRaw(t('A feed named %title already exists.', array('%title' => $feeds[1]['title[0][value]'])), 'Verifying that a duplicate title was identified');
 
     $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
     $this->assertEqual($after, 2, 'Verifying that two distinct feeds were added.');
@@ -115,8 +115,8 @@ function submitImportForm() {
       $refresh = $refresh && $feed->refresh == 900;
     }
 
-    $this->assertEqual($title[$feeds[0]['url']], $feeds[0]['title'], 'First feed was added correctly.');
-    $this->assertEqual($url[$feeds[1]['title']], $feeds[1]['url'], 'Second feed was added correctly.');
+    $this->assertEqual($title[$feeds[0]['url[0][value]']], $feeds[0]['title[0][value]'], 'First feed was added correctly.');
+    $this->assertEqual($url[$feeds[1]['title[0][value]']], $feeds[1]['url[0][value]'], 'Second feed was added correctly.');
     $this->assertTrue($refresh, 'Refresh times are correct.');
   }
 
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php
index dc9c2122e9112e9384906a711ba91c5989772d90..8256d66d699c506887489780f3049d918b500956 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php
@@ -37,17 +37,17 @@ function testUpdateFeedItem() {
 
     // Test updating feed items without valid timestamp information.
     $edit = array(
-      'title' => "Feed without publish timestamp",
-      'url' => $this->getRSS091Sample(),
+      'title[0][value]' => "Feed without publish timestamp",
+      'url[0][value]' => $this->getRSS091Sample(),
     );
 
-    $this->drupalGet($edit['url']);
-    $this->assertResponse(array(200), format_string('URL !url is accessible', array('!url' => $edit['url'])));
+    $this->drupalGet($edit['url[0][value]']);
+    $this->assertResponse(array(200), format_string('URL !url is accessible', array('!url' => $edit['url[0][value]'])));
 
     $this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
-    $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), format_string('The feed !name has been added.', array('!name' => $edit['title'])));
+    $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title[0][value]'])), format_string('The feed !name has been added.', array('!name' => $edit['title[0][value]'])));
 
-    $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url']))->fetchField();
+    $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url[0][value]']))->fetchField();
     $feed = aggregator_feed_load($fid);
 
     $feed->refreshItems();
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php
index de919ec630f65f2c5e536607e08cb909fee8c60e..4bd9bbfd2227f5cad342fa896d92fc72c7d488f6 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php
@@ -23,8 +23,8 @@ public static function getInfo() {
    * Creates a feed and attempts to update it.
    */
   function testUpdateFeed() {
-    $remamining_fields = array('title', 'url', '');
-    foreach ($remamining_fields as $same_field) {
+    $remaining_fields = array('title[0][value]', 'url[0][value]', '');
+    foreach ($remaining_fields as $same_field) {
       $feed = $this->createFeed();
 
       // Get new feed data array and modify newly created feed.
@@ -34,19 +34,19 @@ function testUpdateFeed() {
         $edit[$same_field] = $feed->{$same_field}->value;
       }
       $this->drupalPostForm('aggregator/sources/' . $feed->id() . '/configure', $edit, t('Save'));
-      $this->assertRaw(t('The feed %name has been updated.', array('%name' => $edit['title'])), format_string('The feed %name has been updated.', array('%name' => $edit['title'])));
+      $this->assertRaw(t('The feed %name has been updated.', array('%name' => $edit['title[0][value]'])), format_string('The feed %name has been updated.', array('%name' => $edit['title[0][value]'])));
 
       // Check feed data.
       $this->assertEqual($this->getUrl(), url('aggregator/sources/' . $feed->id(), array('absolute' => TRUE)));
-      $this->assertTrue($this->uniqueFeed($edit['title'], $edit['url']), 'The feed is unique.');
+      $this->assertTrue($this->uniqueFeed($edit['title[0][value]'], $edit['url[0][value]']), 'The feed is unique.');
 
       // Check feed source.
       $this->drupalGet('aggregator/sources/' . $feed->id());
       $this->assertResponse(200, 'Feed source exists.');
-      $this->assertText($edit['title'], 'Page title');
+      $this->assertText($edit['title[0][value]'], 'Page title');
 
       // Delete feed.
-      $feed->title = $edit['title']; // Set correct title so deleteFeed() will work.
+      $feed->title = $edit['title[0][value]']; // Set correct title so deleteFeed() will work.
       $this->deleteFeed($feed);
     }
   }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
index f9be8cb4cd0d39604292b546dee37fa78826c57b..ae28e75b8622cdf11ca4974584002a2bb0350e86 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
@@ -127,7 +127,7 @@ function testModuleEnableOrder() {
     // - taxonomy depends on options
     // - ban depends on xmlrpc (via module_test)
     // The correct enable order is:
-    $expected_order = array('filter', 'xmlrpc', 'ban', 'text', 'node', 'datetime', 'comment', 'history', 'options', 'taxonomy', 'forum');
+    $expected_order = array('filter', 'text', 'options', 'xmlrpc', 'ban', 'node', 'datetime', 'comment', 'history', 'taxonomy', 'forum');
 
     // Enable the modules through the UI, verifying that the dependency chain
     // is correct.
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php
index ca3f04a78be72b6bc2390fad0c5428ddcbe097d0..9ac9de1133504e7b43f762d04aa3130415532a35 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php
@@ -89,11 +89,8 @@ function testAdministratorRole() {
 
     // Enable aggregator module and ensure the 'administer news feeds'
     // permission is assigned by default.
-    $edit = array();
-    $edit['modules[Core][aggregator][enable]'] = TRUE;
-    // Aggregator depends on file module, enable that as well.
-    $edit['modules[Field types][file][enable]'] = TRUE;
-    $this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
+    \Drupal::ModuleHandler()->install(array('aggregator'));
+
     $this->assertTrue($this->admin_user->hasPermission('administer news feeds'), 'The permission was automatically assigned to the administrator role');
   }