diff --git a/core/includes/common.inc b/core/includes/common.inc
index 5572e54..bfee36f 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2641,7 +2641,18 @@ function _drupal_bootstrap_code() {
     // cases.
     $allowed_protocols = array('http', 'https');
   }
+
   UrlHelper::setAllowedProtocols($allowed_protocols);
+
+  // Set the allowed attributes once we have the config available.
+  $allowed_attributes = Drupal::config('system.filter')->get('attributes');
+  if (!isset($allowed_attributes)) {
+    // filter_xss_admin() is called by the installer and update.php, in which
+    // case the configuration may not exist (yet). Provide a minimal default set
+    // of allowed attributes for these cases.
+    $allowed_attributes = array('alt', 'lang', 'rel', 'title', 'value');
+  }
+  Xss::setAllowedAttributes($allowed_attributes);
 }
 
 /**
diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index dc49913..35b314f 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -36,6 +36,25 @@ class Xss {
   protected static $adminTags = array('a', 'abbr', 'acronym', 'address', 'article', 'aside', 'b', 'bdi', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'command', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em', 'figcaption', 'figure', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'img', 'ins', 'kbd', 'li', 'mark', 'menu', 'meter', 'nav', 'ol', 'output', 'p', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'small', 'span', 'strong', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time', 'tr', 'tt', 'u', 'ul', 'var', 'wbr');
 
   /**
+   * The list of Safe HTML attributes.  The default values are the common values from
+   * https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet,
+   * Rule #7.
+   *
+   * @var array
+   */
+  protected static $allowedAttributes = array('alt', 'lang', 'rel', 'title', 'value');
+
+  /**
+   * Sets the allowed and/or Safe HTML attributes.
+   *
+   * @param array $attributes
+   *   An array of attributes, for example alt, title and value.
+   */
+  public static function setAllowedAttributes(array $attributes = array()) {
+    static::$allowedAttributes = $attributes;
+  }
+
+  /**
    * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
    *
    * Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses.
@@ -202,6 +221,7 @@ protected static function split($string, $html_tags, $split_mode) {
    *   Cleaned up version of the HTML attributes.
    */
   protected static function attributes($attributes) {
+    $allowed_attributes = array_flip(static::$allowedAttributes);
     $attributes_array = array();
     $mode = 0;
     $attribute_name = '';
@@ -242,11 +262,19 @@ protected static function attributes($attributes) {
         case 2:
           // Attribute value, a URL after href= for instance.
           if (preg_match('/^"([^"]*)"(\s+|$)/', $attributes, $match)) {
-            $thisval = UrlHelper::filterBadProtocol($match[1]);
+            // If the attribute is listed under the allowed and/or Safe HTML
+            // allowed, then do not check for bad protocols.
+            if (!isset($allowed_attributes[strtolower($attribute_name)])) {
+              $thisval = UrlHelper::filterBadProtocol($match[1]);
+            }
+            else {
+              $thisval = $match[1];
+            }
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
             }
+
             $working = 1;
             $mode = 0;
             $attributes = preg_replace('/^"[^"]*"(\s+|$)/', '', $attributes);
@@ -254,22 +282,33 @@ protected static function attributes($attributes) {
           }
 
           if (preg_match("/^'([^']*)'(\s+|$)/", $attributes, $match)) {
-            $thisval = UrlHelper::filterBadProtocol($match[1]);
-
+            if (!isset($allowed_attributes[strtolower($attribute_name)])) {
+              $thisval = UrlHelper::filterBadProtocol($match[1]);
+            }
+            else {
+              $thisval = $match[1];
+            }
             if (!$skip) {
               $attributes_array[] = "$attribute_name='$thisval'";
             }
+
             $working = 1; $mode = 0;
             $attributes = preg_replace("/^'[^']*'(\s+|$)/", '', $attributes);
             break;
           }
 
           if (preg_match("%^([^\s\"']+)(\s+|$)%", $attributes, $match)) {
-            $thisval = UrlHelper::filterBadProtocol($match[1]);
+            if (!isset($allowed_attributes[strtolower($attribute_name)])) {
+              $thisval = UrlHelper::filterBadProtocol($match[1]);
+            }
+            else {
+              $thisval = $match[1];
+            }
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
             }
+
             $working = 1; $mode = 0;
             $attributes = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attributes);
           }
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 37b93bd..0ebea0c 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -375,6 +375,12 @@ function template_preprocess_block(&$variables) {
   $variables['attributes']['class'][] = 'block';
   $variables['attributes']['class'][] = drupal_html_class('block-' . $variables['configuration']['provider']);
 
+  // Add WAI-ARIA role attributes based on the block category.
+  // @todo: Validate these?  Add a default value?
+  if (isset($variables['block']->category)) {
+    $variables['attributes']['role'] = $variables['block']->category;
+  }
+
   // Add default class for block content.
   $variables['content_attributes']['class'][] = 'content';
 
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 9cc4835..186c2cd 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -362,15 +362,6 @@ function book_form_node_delete_confirm_alter(&$form, $form_state) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block templates.
- */
-function book_preprocess_block(&$variables) {
-  if ($variables['configuration']['provider'] == 'book') {
-    $variables['attributes']['role'] = 'navigation';
-  }
-}
-
-/**
  * Prepares variables for book listing block templates.
  *
  * Default template: book-all-books-block.html.twig.
diff --git a/core/modules/book/src/Plugin/Block/BookNavigationBlock.php b/core/modules/book/src/Plugin/Block/BookNavigationBlock.php
index b36beff..22313c0 100644
--- a/core/modules/book/src/Plugin/Block/BookNavigationBlock.php
+++ b/core/modules/book/src/Plugin/Block/BookNavigationBlock.php
@@ -77,6 +77,7 @@ public static function create(ContainerInterface $container, array $configuratio
    */
   public function defaultConfiguration() {
     return array(
+      'category' => 'navigation',
       'block_mode' => "all pages",
     );
   }
diff --git a/core/modules/field_ui/css/field_ui.admin.css b/core/modules/field_ui/css/field_ui.admin.css
index 29bef62..7b3ea18 100644
--- a/core/modules/field_ui/css/field_ui.admin.css
+++ b/core/modules/field_ui/css/field_ui.admin.css
@@ -63,3 +63,12 @@
 .field-ui-overview .field-plugin-settings-edit-form .plugin-name {
   font-weight: bold;
 }
+
+/* Fieldset variant */
+fieldset.fieldset-no-border {
+  border: none;
+  padding: 0;
+}
+fieldset.fieldset-no-border legend {
+  text-transform: none;
+}
diff --git a/core/modules/field_ui/src/Form/FieldEditForm.php b/core/modules/field_ui/src/Form/FieldEditForm.php
index 068bd71..bdb641b 100644
--- a/core/modules/field_ui/src/Form/FieldEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldEditForm.php
@@ -97,6 +97,10 @@ public function buildForm(array $form, array &$form_state, FieldInstanceConfigIn
 
     // Build the configurable field values.
     $cardinality = $field->getCardinality();
+
+    // Admin specific css.
+    $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.admin.css';
+
     $form['field']['cardinality_container'] = array(
       // Reset #parents to 'field', so the additional container does not appear.
       '#parents' => array('field'),
@@ -105,7 +109,8 @@ public function buildForm(array $form, array &$form_state, FieldInstanceConfigIn
       '#attributes' => array('class' => array(
         'container-inline',
         'fieldgroup',
-        'form-composite'
+        'form-composite',
+        'fieldset-no-border'
       )),
     );
     $form['field']['cardinality_container']['cardinality'] = array(
diff --git a/core/modules/system/config/install/system.filter.yml b/core/modules/system/config/install/system.filter.yml
index 12ce55a..5ce0d73 100644
--- a/core/modules/system/config/install/system.filter.yml
+++ b/core/modules/system/config/install/system.filter.yml
@@ -12,3 +12,46 @@ protocols:
   - sftp
   - webcal
   - rtsp
+attributes:
+  - alt
+  - lang
+  - title
+  - rel
+  - value
+  - align
+  - alink
+  - bgcolor
+  - border
+  - cellpadding
+  - cellspacing
+  - class
+  - color
+  - cols
+  - colspan
+  - coords
+  - dir
+  - face
+  - height
+  - hspace
+  - ismap
+  - marginheight
+  - marginwidth
+  - multiple
+  - nohref
+  - noresize
+  - noshade
+  - nowrap
+  - ref
+  - rev
+  - rows
+  - rowspan
+  - scrolling
+  - shape
+  - span
+  - summary
+  - tabindex
+  - usemap
+  - valign
+  - vlink
+  - vspace
+  - width
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index e2ee096..bf17140 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -153,6 +153,12 @@ system.filter:
       sequence:
         - type: string
           label: 'Protocol'
+    attributes:
+      type: sequence
+      label: 'Allowed attributes'
+      sequence:
+        - type: string
+          label: 'Attribute'
 
 system.logging:
   type: mapping
diff --git a/core/modules/user/src/Plugin/Block/UserLoginBlock.php b/core/modules/user/src/Plugin/Block/UserLoginBlock.php
index 3466c21..a325b54 100644
--- a/core/modules/user/src/Plugin/Block/UserLoginBlock.php
+++ b/core/modules/user/src/Plugin/Block/UserLoginBlock.php
@@ -31,6 +31,15 @@ public function access(AccountInterface $account) {
   }
 
   /**
+   * Overrides \Drupal\block\BlockBase::blockSettings().
+   */
+  public function blockSettings() {
+    return array(
+      'category' => 'form',
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function build() {
