From 3a398b245930ce60ceb51fc605ab0f20c8df1f62 Mon Sep 17 00:00:00 2001 From: Andy Huang Date: Fri, 8 Mar 2013 13:50:36 -0800 Subject: reverse ineffective HTML transforms, add aggressive transform Reverse and
transform work when the resulting width is unchanged, or significantly unchanged (below a threshold). Switch and
transforms to use CSS !important overriding instead of modifying the elements inline. This is easier to reverse. Add another transform to aggressively trigger word wrapping intra- word. This helps break long signatures, but it can render complex promotional email unreadable. I'll have to hide this behind a classifier that prevents this from running on complex messages. Bug: 7400516 Change-Id: Ia17bae2b28eeec9d1cd40d0292380ae5d660586d --- assets/script.js | 109 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 18 deletions(-) (limited to 'assets') diff --git a/assets/script.js b/assets/script.js index acf219002..c4f3ae6d2 100644 --- a/assets/script.js +++ b/assets/script.js @@ -23,6 +23,13 @@ var gImageLoadElements = []; var gScaleInfo; +/** + * Only revert transforms that do an imperfect job of shrinking content if they fail + * to shrink by this much. Expressed as a ratio of: + * (original width difference : width difference after transforms); + */ +TRANSFORM_MINIMUM_EFFECTIVE_RATIO = 0.75; + /** * Returns the page offset of an element. * @@ -188,51 +195,117 @@ function normalizeElementWidths(elements) { function mungeTables(el, docWidth, elWidth) { var nodes; var i, len; - var newWidth; + var newWidth = elWidth; var wStr; - var touched = false; + var touched; + var actionLog = []; + var node; + var start; if (elWidth <= docWidth) { return; } - // first find tables with widths and strip them of widths - nodes = el.querySelectorAll("table[width]"); + + start = Date.now(); + // Try munging all divs with inline styles where the width + // is wider than docWidth, and change it to be a max-width. + touched = false; + nodes = el.querySelectorAll("div[style]"); for (i = 0, len = nodes.length; i < len; i++) { - if (nodes[i].hasAttribute("width")) { - nodes[i].removeAttribute("width"); + node = nodes[i]; + wStr = node.style.width; + if (wStr && wStr.slice(0, -2) > docWidth) { + node.style.width = ""; + node.style.maxWidth = wStr; touched = true; } } + if (touched) { + newWidth = el.scrollWidth; + console.log("ran div-width munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth + + " docW=" + docWidth); + if (newWidth <= docWidth) { + console.log("munger succeeded, elapsed time=" + (Date.now() - start)); + return; + } + } + + // OK, that wasn't enough. Find tables with widths and override their widths. + touched = addClassToElements(el.querySelectorAll("table"), shouldMungeTable, "munged", + actionLog); if (touched) { newWidth = el.scrollWidth; console.log("ran table munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth + " docW=" + docWidth); if (newWidth <= docWidth) { + console.log("munger succeeded, elapsed time=" + (Date.now() - start)); return; } } - // OK, that wasn't enough. Try munging all divs with inline styles where the width - // is wider than docWidth, and change it to be a max-width. - touched = false; - nodes = el.querySelectorAll("div[style]"); - for (i = 0, len = nodes.length; i < len; i++) { - wStr = nodes[i].style.width; - if (wStr && wStr.slice(0, -2) > docWidth) { - nodes[i].style.width = ""; - nodes[i].style.maxWidth = wStr; - touched = true; + // OK, that wasn't enough. Try munging all to override any width and nowrap set. + touched = addClassToElements(el.querySelectorAll("td"), null /* mungeAll */, "munged", + actionLog); + if (touched) { + newWidth = el.scrollWidth; + console.log("ran td munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth + + " docW=" + docWidth); + if (newWidth <= docWidth) { + console.log("munger succeeded, elapsed time=" + (Date.now() - start)); + return; } } + + // OK, that wasn't enough. Try further munging all to override text wrapping. + touched = addClassToElements(el.querySelectorAll("td"), null /* mungeAll */, "munged2", + actionLog); if (touched) { newWidth = el.scrollWidth; - console.log("ran div-width munger on el=" + el + " oldW=" + elWidth + " newW=" + newWidth + console.log("ran td munger2 on el=" + el + " oldW=" + elWidth + " newW=" + newWidth + " docW=" + docWidth); if (newWidth <= docWidth) { + console.log("munger succeeded, elapsed time=" + (Date.now() - start)); return; } } - // TODO: consider reversing changes that didn't help + // If the transformations shrank the width significantly enough, leave them in place. + // We figure that in those cases, the benefits outweight the risk of rendering artifacts. + // + // TODO: this is a risky transform that should not be attempted on sufficiently complex mail. + // (TBD how to measure that) + if ((elWidth - newWidth) / (elWidth - docWidth) > TRANSFORM_MINIMUM_EFFECTIVE_RATIO) { + console.log("transform(s) deemed effective enough. elapsed time=" + + (Date.now() - start)); + return; + } + + // reverse all changes if the width is STILL not narrow enough + // (except the width->maxWidth change, which is not particularly destructive) + for (i = 0, len = actionLog.length; i < len; i++) { + actionLog[i][0].classList.remove(actionLog[i][1]); + } + if (actionLog.length > 0) { + console.log("all mungers failed, changes reversed. elapsed time=" + (Date.now() - start)); + } +} + +function addClassToElements(nodes, conditionFn, classToAdd, actionLog) { + var i, len; + var node; + var added = false; + for (i = 0, len = nodes.length; i < len; i++) { + node = nodes[i]; + if (!conditionFn || conditionFn(node)) { + node.classList.add(classToAdd); + added = true; + actionLog.push([node, classToAdd]); + } + } + return added; +} + +function shouldMungeTable(table) { + return table.hasAttribute("width") || table.style.width; } function hideAllUnsafeImages() { -- cgit v1.2.3