Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /** | 1 /** |
| 2 * HTML Serializer that takes a document and synchronously stores it as an array | 2 * HTML Serializer that takes a document and synchronously stores it as an array |
| 3 * of strings, then asynchronously retrieves data URLs for same-origin images. | 3 * of strings, then asynchronously retrieves data URLs for same-origin images. |
| 4 * It stores enough state to later be converted to an html text file. | 4 * It stores enough state to later be converted to an html text file. |
| 5 */ | 5 */ |
| 6 var HTMLSerializer = class { | 6 var HTMLSerializer = class { |
| 7 constructor() { | 7 constructor() { |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * @private {Set<string>} Contains the tag names that should be | 10 * @private {Set<string>} Contains the tag names that should be |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 63 * text. | 63 * text. |
| 64 * @const | 64 * @const |
| 65 */ | 65 */ |
| 66 this.INPUT_TEXT_TYPE = { | 66 this.INPUT_TEXT_TYPE = { |
| 67 HTML : 0, | 67 HTML : 0, |
| 68 CSS : 1 | 68 CSS : 1 |
| 69 }; | 69 }; |
| 70 | 70 |
| 71 /** | 71 /** |
| 72 * @public {Array<string>} This array represents the serialized html that | 72 * @public {Array<string>} This array represents the serialized html that |
| 73 * makes up a node or document. | 73 * makes up a node or document. |
| 74 */ | 74 */ |
| 75 this.html = []; | 75 this.html = []; |
| 76 | 76 |
| 77 /** | 77 /** |
| 78 * @public {Object<number, string>} The keys represent an index in | 78 * @public {Object<number, string>} The keys represent an index in |
| 79 * |this.html|. The value is a url at which the resource that belongs at | 79 * |this.html|. The value is a url at which the resource that belongs at |
| 80 * that index can be retrieved. The resource will eventually be | 80 * that index can be retrieved. The resource will eventually be |
| 81 * converted to a data url. | 81 * converted to a data url. |
| 82 */ | 82 */ |
| 83 this.srcHoles = {}; | 83 this.srcHoles = {}; |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 180 * @private {string} The assigned id of the html element. | 180 * @private {string} The assigned id of the html element. |
| 181 */ | 181 */ |
| 182 this.rootId; | 182 this.rootId; |
| 183 } | 183 } |
| 184 | 184 |
| 185 /** | 185 /** |
| 186 * Takes an html document, and populates this objects fields such that it can | 186 * Takes an html document, and populates this objects fields such that it can |
| 187 * eventually be converted into an html file. | 187 * eventually be converted into an html file. |
| 188 * | 188 * |
| 189 * @param {Document} doc The Document to serialize. | 189 * @param {Document} doc The Document to serialize. |
| 190 */ | 190 */ |
| 191 processDocument(doc) { | 191 processDocument(doc) { |
| 192 this.windowHeight = doc.defaultView.innerHeight; | 192 this.windowHeight = doc.defaultView.innerHeight; |
| 193 this.windowWidth = doc.defaultView.innerWidth; | 193 this.windowWidth = doc.defaultView.innerWidth; |
| 194 | 194 |
| 195 if (doc.doctype) { | 195 if (doc.doctype) { |
| 196 this.html.push('<!DOCTYPE html>\n'); | 196 this.html.push('<!DOCTYPE html>\n'); |
| 197 } | 197 } |
| 198 | 198 |
| 199 if (this.iframeFullyQualifiedName(doc.defaultView) == '0') { | 199 if (this.iframeFullyQualifiedName(doc.defaultView) == '0') { |
| 200 this.html.push( | 200 this.html.push( |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 224 style = style.replace(/"/g, escapedQuote); | 224 style = style.replace(/"/g, escapedQuote); |
| 225 this.html[this.pseudoElementTestingStyleIndex] = style; | 225 this.html[this.pseudoElementTestingStyleIndex] = style; |
| 226 } | 226 } |
| 227 | 227 |
| 228 /** | 228 /** |
| 229 * Takes an html node, and populates this object's fields such that it can | 229 * Takes an html node, and populates this object's fields such that it can |
| 230 * eventually be converted into an html text file. | 230 * eventually be converted into an html text file. |
| 231 * | 231 * |
| 232 * @param {Node} node The Node to serialize. | 232 * @param {Node} node The Node to serialize. |
| 233 * @private | 233 * @private |
| 234 */ | 234 */ |
| 235 processTree(node) { | 235 processTree(node) { |
| 236 var tagName = node.tagName; | 236 var tagName = node.tagName; |
| 237 if (!tagName && node.nodeType != Node.TEXT_NODE) { | 237 if (!tagName && node.nodeType != Node.TEXT_NODE) { |
| 238 // Ignore nodes that don't have tags and are not text. | 238 // Ignore nodes that don't have tags and are not text. |
| 239 } else if (tagName && this.FILTERED_TAGS.has(tagName)) { | 239 } else if (tagName && this.FILTERED_TAGS.has(tagName)) { |
| 240 // Filter out nodes that are in filteredTags. | 240 // Filter out nodes that are in filteredTags. |
| 241 } else if (node.nodeType == Node.TEXT_NODE) { | 241 } else if (node.nodeType == Node.TEXT_NODE) { |
| 242 this.processText(node); | 242 this.processText(node); |
| 243 } else { | 243 } else { |
| 244 this.html.push(`<${tagName.toLowerCase()} `); | 244 this.html.push(`<${tagName.toLowerCase()} `); |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 329 this.html.push(text); | 329 this.html.push(text); |
| 330 } | 330 } |
| 331 | 331 |
| 332 /** | 332 /** |
| 333 * Takes an html element, and populates this object's fields with the | 333 * Takes an html element, and populates this object's fields with the |
| 334 * appropriate attribute names and values. | 334 * appropriate attribute names and values. |
| 335 * | 335 * |
| 336 * @param {Element} element The Element to serialize. | 336 * @param {Element} element The Element to serialize. |
| 337 * @param {string} id The id of the Element being serialized. | 337 * @param {string} id The id of the Element being serialized. |
| 338 * @private | 338 * @private |
| 339 */ | 339 */ |
| 340 processAttributes(element, id) { | 340 processAttributes(element, id) { |
| 341 var win = element.ownerDocument.defaultView; | 341 var win = element.ownerDocument.defaultView; |
| 342 var style = win.getComputedStyle(element, null); | 342 var style = win.getComputedStyle(element, null); |
| 343 var styleMap = {}; | 343 var styleMap = {}; |
| 344 for (var i = 0; i < style.length; i++) { | 344 for (var i = 0; i < style.length; i++) { |
| 345 var propertyName = style.item(i); | 345 var propertyName = style.item(i); |
| 346 styleMap[propertyName] = style.getPropertyValue(propertyName); | 346 styleMap[propertyName] = style.getPropertyValue(propertyName); |
| 347 } | 347 } |
| 348 this.idToStyleMap[id] = styleMap; | 348 this.idToStyleMap[id] = styleMap; |
| 349 this.idToStyleIndex[id] = this.html.length; | 349 this.idToStyleIndex[id] = this.html.length; |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 576 * | 576 * |
| 577 * @param {Window} win The window to use in the calculation. | 577 * @param {Window} win The window to use in the calculation. |
| 578 * @return {string} The full path. | 578 * @return {string} The full path. |
| 579 */ | 579 */ |
| 580 iframeFullyQualifiedName(win) { | 580 iframeFullyQualifiedName(win) { |
| 581 if (this.iframeIndex(win) < 0) { | 581 if (this.iframeIndex(win) < 0) { |
| 582 return '0'; | 582 return '0'; |
| 583 } else { | 583 } else { |
| 584 var fullyQualifiedName = this.iframeFullyQualifiedName(win.parent); | 584 var fullyQualifiedName = this.iframeFullyQualifiedName(win.parent); |
| 585 var index = this.iframeIndex(win); | 585 var index = this.iframeIndex(win); |
| 586 return fullyQualifiedName + '.' + index; | 586 return fullyQualifiedName + '.' + index; |
| 587 } | 587 } |
| 588 } | 588 } |
| 589 | 589 |
| 590 /** | 590 /** |
| 591 * Calculate the correct encoding of a character that should be used given the | 591 * Calculate the correct encoding of a character that should be used given the |
| 592 * nesting depth of the window in the frame tree. | 592 * nesting depth of the window in the frame tree. |
| 593 * | 593 * |
| 594 * @param {string} char The character that should be escaped. | 594 * @param {string} char The character that should be escaped. |
| 595 * @param {number} depth The nesting depth of the appropriate window in the | 595 * @param {number} depth The nesting depth of the appropriate window in the |
| 596 * frame tree. | 596 * frame tree. |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 717 serializer.processCSSFonts(doc.defaultView, styleSheetSrc, css); | 717 serializer.processCSSFonts(doc.defaultView, styleSheetSrc, css); |
| 718 serializer.fillFontHoles(doc, callback); | 718 serializer.fillFontHoles(doc, callback); |
| 719 }).catch(function(error) { | 719 }).catch(function(error) { |
| 720 console.log(error); | 720 console.log(error); |
| 721 serializer.fillFontHoles(doc, callback); | 721 serializer.fillFontHoles(doc, callback); |
| 722 }); | 722 }); |
| 723 } | 723 } |
| 724 } | 724 } |
| 725 | 725 |
| 726 /** | 726 /** |
| 727 * Return a JSONizable dictionary that contain all the data of this object. | |
| 728 */ | |
| 729 asDict() { | |
|
wkorman
2017/09/20 20:11:04
Add a unit test for this? May as well while we're
nednguyen
2017/09/26 13:01:35
Acknowledged. I filed a tracking bug here https://
| |
| 730 return { | |
| 731 'html': htmlSerializer.html, | |
| 732 'frameHoles': htmlSerializer.frameHoles, | |
| 733 'idToStyleIndex': htmlSerializer.idToStyleIndex, | |
| 734 'idToStyleMap': htmlSerializer.idToStyleMap, | |
| 735 'windowHeight': htmlSerializer.windowHeight, | |
| 736 'windowWidth': htmlSerializer.windowWidth, | |
| 737 'rootId': htmlSerializer.rootId, | |
| 738 'rootStyleIndex': htmlSerializer.rootStyleIndex, | |
| 739 'pseudoElementSelectorToCSSMap': | |
| 740 htmlSerializer.pseudoElementSelectorToCSSMap, | |
| 741 'pseudoElementPlaceHolderIndex': | |
| 742 htmlSerializer.pseudoElementPlaceHolderIndex, | |
| 743 'pseudoElementTestingStyleIndex': | |
| 744 htmlSerializer.pseudoElementStyleTestingIndex, | |
| 745 'pseudoElementTestingStyleId': htmlSerializer.pseudoElementTestingStyleId, | |
| 746 'unusedId': htmlSerializer.generateId(document), | |
| 747 'frameIndex': htmlSerializer.iframeFullyQualifiedName(window) | |
| 748 }; | |
| 749 } | |
| 750 | |
| 751 /** | |
| 727 * Take all of the srcHoles and create data urls for the resources, placing | 752 * Take all of the srcHoles and create data urls for the resources, placing |
| 728 * them in |this.html|. Calls the callback when complete. | 753 * them in |this.html|. Calls the callback when complete. |
| 729 * | 754 * |
| 730 * @param {Function} callback The callback function. | 755 * @param {Function} callback The callback function. |
| 731 */ | 756 */ |
| 732 fillSrcHoles(callback) { | 757 fillSrcHoles(callback) { |
| 733 if (Object.keys(this.srcHoles).length == 0) { | 758 if (Object.keys(this.srcHoles).length == 0) { |
| 734 callback(this); | 759 callback(this); |
| 735 } else { | 760 } else { |
| 736 var index = Object.keys(this.srcHoles)[0]; | 761 var index = Object.keys(this.srcHoles)[0]; |
| 737 var src = this.srcHoles[index]; | 762 var src = this.srcHoles[index]; |
| 738 delete this.srcHoles[index]; | 763 delete this.srcHoles[index]; |
| 739 var serializer = this; | 764 var serializer = this; |
| 740 fetch(src).then(function(response) { | 765 fetch(src).then(function(response) { |
| 741 return response.blob(); | 766 return response.blob(); |
| 742 }).then(function(blob) { | 767 }).then(function(blob) { |
| 743 var reader = new FileReader(); | 768 var reader = new FileReader(); |
| 744 reader.onload = function(e) { | 769 reader.onload = function(e) { |
| 745 serializer.html[index] = e.target.result; | 770 serializer.html[index] = e.target.result; |
| 746 serializer.fillSrcHoles(callback); | 771 serializer.fillSrcHoles(callback); |
| 747 } | 772 } |
| 748 reader.readAsDataURL(blob); | 773 reader.readAsDataURL(blob); |
| 749 }).catch(function(error) { | 774 }).catch(function(error) { |
| 750 console.log(error); | 775 console.log(error); |
| 751 serializer.fillSrcHoles(callback); | 776 serializer.fillSrcHoles(callback); |
| 752 }); | 777 }); |
| 753 } | 778 } |
| 754 } | 779 } |
| 755 } | 780 } |
| OLD | NEW |