diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
index 2cd6254..1b5d90a 100644
--- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
@@ -25,18 +25,44 @@
       // additional data-entity-type and data-entity-uuid attributes.
       editor.on('widgetDefinition', function (event) {
         var widgetDefinition = event.data;
+        var i;
         if (widgetDefinition.name !== 'image') {
           return;
         }
 
         // Override requiredContent & allowedContent.
-        widgetDefinition.requiredContent = 'img[alt,src,width,height,data-entity-type,data-entity-uuid]';
-        widgetDefinition.allowedContent.img.attributes += ',!data-entity-type,!data-entity-uuid';
-        // We don't allow <figure>, <figcaption>, <div> or <p>  in our downcast.
-        delete widgetDefinition.allowedContent.figure;
-        delete widgetDefinition.allowedContent.figcaption;
-        delete widgetDefinition.allowedContent.div;
-        delete widgetDefinition.allowedContent.p;
+        widgetDefinition.requiredContent = new CKEDITOR.style({
+          element: 'img',
+          attributes: {
+            'alt': '',
+            'src': '',
+            'width': '',
+            'height': '',
+            'data-entity-type': '',
+            'data-entity-uuid': ''
+          }
+        });
+        var allowedContentDefinition = {
+          element: 'img',
+          attributes: {
+            '!data-entity-type': '',
+            '!data-entity-uuid': ''
+          }
+        };
+        var imgAttributes = widgetDefinition.allowedContent.img.attributes.split(/\s*,\s*/);
+        for (i = 0; i < imgAttributes.length; i++) {
+          allowedContentDefinition.attributes[imgAttributes[i]] = '';
+        }
+        if (widgetDefinition.allowedContent.img.classes) {
+          allowedContentDefinition.attributes['class'] = widgetDefinition.allowedContent.img.classes.split(/\s*,\s*/).join(' ');
+        }
+        if (widgetDefinition.allowedContent.img.styles) {
+          var imgStyles = widgetDefinition.allowedContent.img.styles.split(/\s*,\s*/);
+          for (i = 0; i < imgStyles.length; i++) {
+            allowedContentDefinition.styles[imgStyles[i].trim()] = '';
+          }
+        }
+        widgetDefinition.allowedContent = new CKEDITOR.style(allowedContentDefinition);
 
         // Override the 'link' part, to completely disable image2's link
         // support: http://dev.ckeditor.com/ticket/11341.
diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
index e68c809..0963c04 100644
--- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
@@ -51,8 +51,14 @@
         }, true);
 
         // Override requiredContent & allowedContent.
-        widgetDefinition.requiredContent = 'img[alt,src,width,height,data-entity-type,data-entity-uuid,data-align,data-caption]';
-        widgetDefinition.allowedContent.img.attributes += ',!data-align,!data-caption';
+        var requiredContent = widgetDefinition.requiredContent.getDefinition();
+        requiredContent.attributes['data-align'] = '';
+        requiredContent.attributes['data-caption'] = '';
+        widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
+        var allowedContent = widgetDefinition.allowedContent.getDefinition();
+        allowedContent.attributes['!data-align'] = '';
+        allowedContent.attributes['!data-caption'] = '';
+        widgetDefinition.allowedContent = new CKEDITOR.style(allowedContent);
 
         // Override allowedContent setting for the 'caption' nested editable.
         // This must match what caption_filter enforces.
@@ -63,9 +69,12 @@
         // Override downcast(): ensure we *only* output <img>, but also ensure
         // we include the data-entity-type, data-entity-uuid, data-align and
         // data-caption attributes.
+        var originalDowncast = widgetDefinition.downcast;
         widgetDefinition.downcast = function (element) {
-          // Find an image element in the one being downcasted (can be itself).
-          var img = findElementByName(element, 'img');
+          var img = originalDowncast.call(this, element);
+          if (!img) {
+            img = findElementByName(element, 'img');
+          }
           var caption = this.editables.caption;
           var captionHtml = caption && caption.getData();
           var attrs = img.attributes;
@@ -94,6 +103,7 @@
         //   - <img> tag in a paragraph (non-captioned, centered image),
         //   - <figure> tag (captioned image).
         // We take the same attributes into account as downcast() does.
+        var originalUpcast = widgetDefinition.upcast;
         widgetDefinition.upcast = function (element, data) {
           if (element.name !== 'img' || !element.attributes['data-entity-type'] || !element.attributes['data-entity-uuid']) {
             return;
@@ -103,6 +113,7 @@
             return;
           }
 
+          element = originalUpcast.call(this, element, data);
           var attrs = element.attributes;
           var retElement = element;
           var caption;
diff --git a/core/modules/ckeditor/js/plugins/drupallink/plugin.js b/core/modules/ckeditor/js/plugins/drupallink/plugin.js
index 4d0832a..e595815 100644
--- a/core/modules/ckeditor/js/plugins/drupallink/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupallink/plugin.js
@@ -13,8 +13,19 @@
     init: function (editor) {
       // Add the commands for link and unlink.
       editor.addCommand('drupallink', {
-        allowedContent: 'a[!href,target]',
-        requiredContent: 'a[href]',
+        allowedContent: new CKEDITOR.style({
+          element: 'a',
+          attributes: {
+            '!href': '',
+            'target': ''
+          }
+        }),
+        requiredContent: new CKEDITOR.style({
+          element: 'a',
+          attributes: {
+            href: ''
+          }
+        }),
         modes: {wysiwyg: 1},
         canUndo: true,
         exec: function (editor) {
@@ -111,8 +122,19 @@
       editor.addCommand('drupalunlink', {
         contextSensitive: 1,
         startDisabled: 1,
-        allowedContent: 'a[!href]',
-        requiredContent: 'a[href]',
+        allowedContent: new CKEDITOR.style({
+          element: 'a',
+          attributes: {
+            '!href': '',
+            'target': ''
+          }
+        }),
+        requiredContent: new CKEDITOR.style({
+          element: 'a',
+          attributes: {
+            href: ''
+          }
+        }),
         exec: function (editor) {
           var style = new CKEDITOR.style({element: 'a', type: CKEDITOR.STYLE_INLINE, alwaysRemoveElement: 1});
           editor.removeStyle(style);
