Bug 1356300 - shorten data URL for background images. r=aceman,jorgk a=jorgk
--- a/editor/ui/composer/content/editorUtilities.js
+++ b/editor/ui/composer/content/editorUtilities.js
@@ -931,8 +931,84 @@ function Clone(obj)
{
if (typeof obj[i] == 'object')
clone[i] = Clone(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
+
+/**
+ * Utility funtions to handle shortended data: URLs in EdColorProps.js and EdImageOverlay.js.
+ */
+
+/**
+ * Is the passed in image URI a shortened data URI?
+ * @return {bool}
+ */
+function isImageDataShortened(aImageData) {
+ return (/^data:/i.test(aImageData) && aImageData.includes("…"));
+}
+
+/**
+ * Event handler for Copy or Cut
+ * @param aEvent the event
+ */
+function onCopyOrCutShortened(aEvent) {
+ // Put the original data URI onto the clipboard in case the value
+ // is a shortened data URI.
+ let field = aEvent.target;
+ let startPos = field.selectionStart;
+ if (startPos == undefined)
+ return;
+ let endPos = field.selectionEnd;
+ let selection = field.value.substring(startPos, endPos).trim();
+
+ // Test that a) the user selected the whole value,
+ // b) the value is a data URI,
+ // c) it contains the ellipsis we added. Otherwise it could be
+ // a new value that the user pasted in.
+ if (selection == field.value.trim() && isImageDataShortened(selection)) {
+ aEvent.clipboardData.setData("text/plain", field.fullDataURI);
+ if (aEvent.type == "cut") {
+ // We have to cut the selection manually. Since we tested that
+ // everything was selected, we can just reset the field.
+ field.value = "";
+ }
+ aEvent.preventDefault();
+ }
+}
+
+/**
+ * Set up element showing an image URI with a shortened version.
+ * and add event handler for Copy or Cut.
+ *
+ * @param aImageData the data: URL of the image to be shortened.
+ * Note: Original stored in 'aDialogField.fullDataURI'.
+ * @param aDialogField The field of the dialog to contain the data.
+ * @return {bool} URL was shortened?
+ */
+function shortenImageData(aImageData, aDialogField) {
+ let shortened = false;
+ aDialogField.value = aImageData.replace(/^(data:.+;base64,)(.*)/i,
+ function(match, nonDataPart, dataPart) {
+ if (dataPart.length <= 35)
+ return match;
+
+ shortened = true;
+ aDialogField.addEventListener("copy", onCopyOrCutShortened);
+ aDialogField.addEventListener("cut", onCopyOrCutShortened);
+ aDialogField.fullDataURI = aImageData;
+ return nonDataPart + dataPart.substring(0, 5) + "…" +
+ dataPart.substring(dataPart.length - 30);
+ });
+ return shortened;
+}
+
+/**
+ * Return full data URIs for a shortened element.
+ *
+ * @param aDialogField The field of the dialog containing the data.
+ */
+function restoredImageData(aDialogField) {
+ return aDialogField.fullDataURI;
+}
--- a/editor/ui/dialogs/content/EdColorProps.js
+++ b/editor/ui/dialogs/content/EdColorProps.js
@@ -103,20 +103,21 @@ function Startup()
function InitDialog()
{
// Get image from document
gBackgroundImage = GetHTMLOrCSSStyleValue(globalElement, backgroundStr, cssBackgroundImageStr);
if (/url\((.*)\)/.test( gBackgroundImage ))
gBackgroundImage = RegExp.$1;
- gDialog.BackgroundImageInput.value = gBackgroundImage;
-
- if (gBackgroundImage)
+ if (gBackgroundImage) {
+ // Shorten data URIs for display.
+ shortenImageData(gBackgroundImage, gDialog.BackgroundImageInput);
gDialog.ColorPreview.setAttribute(styleStr, backImageStyle+gBackgroundImage+");");
+ }
SetRelativeCheckbox();
customTextColor = GetHTMLOrCSSStyleValue(globalElement, textStr, cssColorStr);
customTextColor = ConvertRGBColorIntoHEXColor(customTextColor);
customLinkColor = globalElement.getAttribute(linkStr);
customActiveColor = globalElement.getAttribute(alinkStr);
customVisitedColor = globalElement.getAttribute(vlinkStr);
@@ -331,23 +332,33 @@ function ValidateAndPreviewImage(ShowErr
{
// First make a string with just background color
var styleValue = backColorStyle+previewBGColor+";";
var retVal = true;
var image = TrimString(gDialog.BackgroundImageInput.value);
if (image)
{
- gBackgroundImage = image;
+ if (isImageDataShortened(image))
+ {
+ gBackgroundImage = restoredImageData(gDialog.BackgroundImageInput);
+ }
+ else
+ {
+ gBackgroundImage = image;
- // Display must use absolute URL if possible
- var displayImage = gHaveDocumentUrl ? MakeAbsoluteUrl(image) : image;
- styleValue += backImageStyle+displayImage+");";
+ // Display must use absolute URL if possible
+ var displayImage = gHaveDocumentUrl ? MakeAbsoluteUrl(image) : image;
+ styleValue += backImageStyle+displayImage+");";
+ }
}
- else gBackgroundImage = null;
+ else
+ {
+ gBackgroundImage = null;
+ }
// Set style on preview (removes image if not valid)
gDialog.ColorPreview.setAttribute(styleStr, styleValue);
// Note that an "empty" string is valid
return retVal;
}
--- a/editor/ui/dialogs/content/EdImageOverlay.js
+++ b/editor/ui/dialogs/content/EdImageOverlay.js
@@ -25,18 +25,16 @@ var gCanRemoveImageMap = false;
var gRemoveImageMap = false;
var gImageMapDisabled = false;
var gActualWidth = "";
var gActualHeight = "";
var gOriginalSrc = "";
var gHaveDocumentUrl = false;
var gTimerID;
var gValidateTab;
-var gFullDataURI = null;
-var gListenerAttached = false;
// These must correspond to values in EditorDialog.css for each theme
// (unfortunately, setting "style" attribute here doesn't work!)
var gPreviewImageWidth = 80;
var gPreviewImageHeight = 50;
// dialog initialization code
@@ -65,64 +63,31 @@ function ImageStartup()
gDialog.ImageHolder = document.getElementById( "preview-image-holder" );
gDialog.PreviewWidth = document.getElementById( "PreviewWidth" );
gDialog.PreviewHeight = document.getElementById( "PreviewHeight" );
gDialog.PreviewSize = document.getElementById( "PreviewSize" );
gDialog.PreviewImage = null;
gDialog.OkButton = document.documentElement.getButton("accept");
}
-function onCopyOrCut(event) {
- // Put the original data URI onto the clipboard in case the value
- // is a shortened data URI.
- let startPos = gDialog.srcInput.selectionStart;
- if (startPos == undefined)
- return;
- let endPos = gDialog.srcInput.selectionEnd;
- let selection = gDialog.srcInput.value.substring(startPos, endPos).trim();
-
- // Test that a) the user selected the whole value,
- // b) the value is a data URI,
- // c) it contains the ellipsis we added. Otherwise it could be
- // a new value that the user pasted in.
- if (selection == gDialog.srcInput.value.trim() &&
- /^data:/i.test(selection) && selection.includes("…")) {
- event.clipboardData.setData("text/plain", gFullDataURI);
- if (event.type == "cut") {
- // We have to cut the selection manually. Since we tested that
- // everything was selected, we can just reset the field.
- gDialog.srcInput.value = "";
- }
- event.preventDefault();
- }
-}
-
// Set dialog widgets with attribute data
// We get them from globalElement copy so this can be used
// by AdvancedEdit(), which is shared by all property dialogs
function InitImage()
{
// Set the controls to the image's attributes
var src = globalElement.getAttribute("src");
// For image insertion the 'src' attribute is null.
if (src) {
// Shorten data URIs for display.
- gDialog.srcInput.value = src.replace(/(data:.+;base64,)(.*)/i,
- function(match, nonDataPart, dataPart) {
- if (!gListenerAttached) {
- gDialog.srcInput.addEventListener("copy", onCopyOrCut);
- gDialog.srcInput.addEventListener("cut", onCopyOrCut);
- gListenerAttached = true;
- }
- gDialog.srcInput.setAttribute("tooltip", "shortenedDataURI");
- gFullDataURI = src;
- return nonDataPart + dataPart.substring(0, 5) + "…" +
- dataPart.substring(dataPart.length - 30);
- });
+ if (shortenImageData(src, gDialog.srcInput)) {
+ gDialog.srcInput.removeAttribute("tooltiptext");
+ gDialog.srcInput.setAttribute("tooltip", "shortenedDataURI");
+ }
}
// Set "Relativize" checkbox according to current URL state
SetRelativeCheckbox();
// Force loading of image from its source and show preview image
LoadPreviewImage();
@@ -317,20 +282,19 @@ function LoadPreviewImage()
{
gDialog.PreviewSize.collapsed = true;
// XXXbz workaround for bug 265416 / bug 266284
gDialog.ImageHolder.collapsed = true;
var imageSrc = TrimString(gDialog.srcInput.value);
if (!imageSrc)
return;
- if (/^data:/i.test(imageSrc) && imageSrc.includes("…"))
+ if (isImageDataShortened(imageSrc))
{
- // Restore URIs from the original.
- imageSrc = gFullDataURI;
+ imageSrc = restoredImageData(gDialog.srcInput);
}
try {
// Remove the image URL from image cache so it loads fresh
// (if we don't do this, loads after the first will always use image cache
// and we won't see image edit changes or be able to get actual width and height)
// We must have an absolute URL to preview it or remove it from the cache
@@ -503,20 +467,19 @@ function ValidateImage()
SwitchToValidatePanel();
gDialog.srcInput.focus();
return false;
}
// We must convert to "file:///" or "http://" format else image doesn't load!
let src = gDialog.srcInput.value.trim();
- if (/^data:/i.test(src) && src.includes("…"))
+ if (isImageDataShortened(src))
{
- // Restore shortened URIs from the original.
- src = gFullDataURI;
+ src = restoredImageData(gDialog.srcInput);
}
else
{
var checkbox = document.getElementById("MakeRelativeCheckbox");
try
{
if (checkbox && !checkbox.checked)
{
--- a/editor/ui/dialogs/content/EdImageOverlay.xul
+++ b/editor/ui/dialogs/content/EdImageOverlay.xul
@@ -21,17 +21,18 @@
<label control = "srcInput"
value = "&locationEditField.label;"
accesskey="&locationEditField.accessKey;"
tooltiptext="&locationEditField.tooltip;"
/>
<tooltip id="shortenedDataURI">
<label value="&locationEditField.shortenedDataURI;"/>
</tooltip>
- <textbox id="srcInput" oninput="ChangeImageSrc();" tabindex="1" class="uri-element"/>
+ <textbox id="srcInput" oninput="ChangeImageSrc();" tabindex="1" class="uri-element"
+ tooltiptext="&locationEditField.tooltip;"/>
<hbox id="MakeRelativeHbox">
<checkbox
id = "MakeRelativeCheckbox"
for = "srcInput"
tabindex="2"/>
<!-- mail compose will insert custom item here defined in mailComposeEditorOverlay.xul -->
<spacer flex="1"/>
<!-- from EdDialogOverlay.xul -->