diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
index ecd678802e411c639d5f812f2132e3a2650adf0e..1de01b0331e0f19eabc218e1358ef04065227470 100644
--- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
@@ -26,15 +26,23 @@ CKEDITOR.plugins.add('drupalimage', {
           existingValues.width = imageDOMElement ? imageDOMElement.width : '';
           existingValues.height = imageDOMElement ? imageDOMElement.height : '';
           // Populate all other attributes by their specified attribute values.
-          var attribute = null;
+          var attribute = null, attributeName;
           for (var key = 0; key < imageDOMElement.attributes.length; key++) {
             attribute = imageDOMElement.attributes.item(key);
-            existingValues[attribute.nodeName.toLowerCase()] = attribute.nodeValue;
+            attributeName = attribute.nodeName.toLowerCase();
+            // Don't consider data-cke-saved- attributes; they're just there to
+            // work around browser quirks.
+            if (attributeName.substring(0, 15) === 'data-cke-saved-') {
+              continue;
+            }
+            // Store the value for this attribute, unless there's a
+            // data-cke-saved- alternative for it, which will contain the quirk-
+            // free, original value.
+            existingValues[attributeName] = imageElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
           }
         }
 
         function saveCallback (returnValues) {
-          // Save snapshot for undo support.
           editor.fire('saveSnapshot');
 
           // Create a new image element if needed.
@@ -53,7 +61,9 @@ CKEDITOR.plugins.add('drupalimage', {
               if (returnValues.attributes.hasOwnProperty(key)) {
                 // Update the property if a value is specified.
                 if (returnValues.attributes[key].length > 0) {
-                  imageElement.setAttribute(key, returnValues.attributes[key]);
+                  var value = returnValues.attributes[key];
+                  imageElement.data('cke-saved-' + key, value);
+                  imageElement.setAttribute(key, value);
                 }
                 // Delete the property if set to an empty string.
                 else {
@@ -62,6 +72,9 @@ CKEDITOR.plugins.add('drupalimage', {
               }
             }
           }
+
+          // Save snapshot for undo support.
+          editor.fire('saveSnapshot');
         }
 
         // Drupal.t() will not work inside CKEditor plugins because CKEditor
diff --git a/core/modules/ckeditor/js/plugins/drupallink/plugin.js b/core/modules/ckeditor/js/plugins/drupallink/plugin.js
index 05a062bd4208489bd0fbfaee23617ae3695f7c54..33c32f9dffbdb146773cd8d01c1e904627b2140d 100644
--- a/core/modules/ckeditor/js/plugins/drupallink/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupallink/plugin.js
@@ -25,16 +25,24 @@ CKEDITOR.plugins.add('drupallink', {
           linkDOMElement = linkElement.$;
 
           // Populate an array with the link's current attributes.
-          var attribute = null;
+          var attribute = null, attributeName;
           for (var key = 0; key < linkDOMElement.attributes.length; key++) {
             attribute = linkDOMElement.attributes.item(key);
-            existingValues[attribute.nodeName.toLowerCase()] = attribute.nodeValue;
+            attributeName = attribute.nodeName.toLowerCase();
+            // Don't consider data-cke-saved- attributes; they're just there to
+            // work around browser quirks.
+            if (attributeName.substring(0, 15) === 'data-cke-saved-') {
+              continue;
+            }
+            // Store the value for this attribute, unless there's a
+            // data-cke-saved- alternative for it, which will contain the quirk-
+            // free, original value.
+            existingValues[attributeName] = linkElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
           }
         }
 
         // Prepare a save callback to be used upon saving the dialog.
         var saveCallback = function (returnValues) {
-          // Save snapshot for undo support.
           editor.fire('saveSnapshot');
 
           // Create a new link element if needed.
@@ -65,7 +73,9 @@ CKEDITOR.plugins.add('drupallink', {
               if (returnValues.attributes.hasOwnProperty(key)) {
                 // Update the property if a value is specified.
                 if (returnValues.attributes[key].length > 0) {
-                  linkElement.setAttribute(key, returnValues.attributes[key]);
+                  var value = returnValues.attributes[key];
+                  linkElement.data('cke-saved-' + key, value);
+                  linkElement.setAttribute(key, value);
                 }
                 // Delete the property if set to an empty string.
                 else {
@@ -74,6 +84,9 @@ CKEDITOR.plugins.add('drupallink', {
               }
             }
           }
+
+          // Save snapshot for undo support.
+          editor.fire('saveSnapshot');
         };
         // Drupal.t() will not work inside CKEditor plugins because CKEditor
         // loads the JavaScript file instead of Drupal. Pull translated strings