Skip to content

Commit 9b372a4

Browse files
committed
Correctly clone <select> and <textarea> value property
1 parent 4985279 commit 9b372a4

6 files changed

Lines changed: 201 additions & 80 deletions

File tree

dist/html2canvas.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ window.html2canvas = function(nodeList, options) {
581581
options.javascriptEnabled = typeof(options.javascriptEnabled) === "undefined" ? false : options.javascriptEnabled;
582582
options.imageTimeout = typeof(options.imageTimeout) === "undefined" ? 10000 : options.imageTimeout;
583583
options.renderer = typeof(options.renderer) === "function" ? options.renderer : CanvasRenderer;
584+
options.strict = !!options.strict;
584585

585586
if (typeof(nodeList) === "string") {
586587
if (typeof(options.proxy) !== "string") {
@@ -705,8 +706,12 @@ function createWindowClone(ownerDocument, containerDocument, width, height, opti
705706

706707
return new Promise(function(resolve) {
707708
var documentClone = container.contentWindow.document;
709+
710+
cloneNodeValues(ownerDocument.documentElement, documentElement, "textarea");
711+
cloneNodeValues(ownerDocument.documentElement, documentElement, "select");
712+
708713
/* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle
709-
if window url is about:blank, we can assign the url to current by writing onto the document
714+
if window url is about:blank, we can assign the url to current by writing onto the document
710715
*/
711716
container.contentWindow.onload = container.onload = function() {
712717
var interval = setInterval(function() {
@@ -733,6 +738,15 @@ function createWindowClone(ownerDocument, containerDocument, width, height, opti
733738
});
734739
}
735740

741+
function cloneNodeValues(document, clone, nodeName) {
742+
var originalNodes = document.getElementsByTagName(nodeName);
743+
var clonedNodes = clone.getElementsByTagName(nodeName);
744+
var count = originalNodes.length;
745+
for (var i = 0; i < count; i++) {
746+
clonedNodes[i].value = originalNodes[i].value;
747+
}
748+
}
749+
736750
function restoreOwnerScroll(ownerDocument, x, y) {
737751
if (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset) {
738752
ownerDocument.defaultView.scrollTo(x, y);
@@ -2245,7 +2259,8 @@ NodeParser.prototype.paintRadio = function(container) {
22452259
};
22462260

22472261
NodeParser.prototype.paintFormValue = function(container) {
2248-
if (container.getValue().length > 0) {
2262+
var value = container.getValue();
2263+
if (value.length > 0) {
22492264
var document = container.node.ownerDocument;
22502265
var wrapper = document.createElement('html2canvaswrapper');
22512266
var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color',
@@ -2265,7 +2280,7 @@ NodeParser.prototype.paintFormValue = function(container) {
22652280
wrapper.style.position = "fixed";
22662281
wrapper.style.left = bounds.left + "px";
22672282
wrapper.style.top = bounds.top + "px";
2268-
wrapper.textContent = container.getValue();
2283+
wrapper.textContent = value;
22692284
document.body.appendChild(wrapper);
22702285
this.paintText(new TextContainer(wrapper.firstChild, container));
22712286
document.body.removeChild(wrapper);

dist/html2canvas.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ window.html2canvas = function(nodeList, options) {
1717
options.javascriptEnabled = typeof(options.javascriptEnabled) === "undefined" ? false : options.javascriptEnabled;
1818
options.imageTimeout = typeof(options.imageTimeout) === "undefined" ? 10000 : options.imageTimeout;
1919
options.renderer = typeof(options.renderer) === "function" ? options.renderer : CanvasRenderer;
20+
options.strict = !!options.strict;
2021

2122
if (typeof(nodeList) === "string") {
2223
if (typeof(options.proxy) !== "string") {
@@ -141,8 +142,12 @@ function createWindowClone(ownerDocument, containerDocument, width, height, opti
141142

142143
return new Promise(function(resolve) {
143144
var documentClone = container.contentWindow.document;
145+
146+
cloneNodeValues(ownerDocument.documentElement, documentElement, "textarea");
147+
cloneNodeValues(ownerDocument.documentElement, documentElement, "select");
148+
144149
/* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle
145-
if window url is about:blank, we can assign the url to current by writing onto the document
150+
if window url is about:blank, we can assign the url to current by writing onto the document
146151
*/
147152
container.contentWindow.onload = container.onload = function() {
148153
var interval = setInterval(function() {
@@ -169,6 +174,15 @@ function createWindowClone(ownerDocument, containerDocument, width, height, opti
169174
});
170175
}
171176

177+
function cloneNodeValues(document, clone, nodeName) {
178+
var originalNodes = document.getElementsByTagName(nodeName);
179+
var clonedNodes = clone.getElementsByTagName(nodeName);
180+
var count = originalNodes.length;
181+
for (var i = 0; i < count; i++) {
182+
clonedNodes[i].value = originalNodes[i].value;
183+
}
184+
}
185+
172186
function restoreOwnerScroll(ownerDocument, x, y) {
173187
if (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset) {
174188
ownerDocument.defaultView.scrollTo(x, y);

src/nodeparser.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,8 @@ NodeParser.prototype.paintRadio = function(container) {
388388
};
389389

390390
NodeParser.prototype.paintFormValue = function(container) {
391-
if (container.getValue().length > 0) {
391+
var value = container.getValue();
392+
if (value.length > 0) {
392393
var document = container.node.ownerDocument;
393394
var wrapper = document.createElement('html2canvaswrapper');
394395
var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color',
@@ -408,7 +409,7 @@ NodeParser.prototype.paintFormValue = function(container) {
408409
wrapper.style.position = "fixed";
409410
wrapper.style.left = bounds.left + "px";
410411
wrapper.style.top = bounds.top + "px";
411-
wrapper.textContent = container.getValue();
412+
wrapper.textContent = value;
412413
document.body.appendChild(wrapper);
413414
this.paintText(new TextContainer(wrapper.firstChild, container));
414415
document.body.removeChild(wrapper);

tests/mocha/form-rendering.html

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<html>
2+
<head>
3+
<meta charset="utf-8">
4+
<title>Mocha Tests</title>
5+
<link rel="stylesheet" href="lib/mocha.css" />
6+
<script src="../../dist/html2canvas.js"></script>
7+
<script src="../../src/log.js"></script>
8+
<script src="../../src/renderer.js"></script>
9+
<script src="../../src/renderers/canvas.js"></script>
10+
<script src="../assets/jquery-1.6.2.js"></script>
11+
<script src="lib/expect.js"></script>
12+
<script src="lib/mocha.js"></script>
13+
<style>
14+
.block {
15+
width: 200px;
16+
height: 200px;
17+
}
18+
</style>
19+
</head>
20+
<body>
21+
<div id="mocha"></div>
22+
<script>mocha.setup('bdd')</script>
23+
<div id="block1" class="block">
24+
<input type="text" value="text" />
25+
</div>
26+
<div id="block2" class="block">
27+
<input type="password" value="password" />
28+
</div>
29+
<div id="block3" class="block">
30+
<input type="text" value="text" />
31+
</div>
32+
33+
<div id="block4" class="block">
34+
<textarea>text</textarea>
35+
</div>
36+
37+
<div id="block5" class="block">
38+
<select>
39+
<option value="1">1</option>
40+
<option value="2" selected>2</option>
41+
<option value="3">3</option>
42+
</select>
43+
</div>
44+
45+
<div id="green-block"></div>
46+
<script>
47+
describe("Rendering input values", function() {
48+
it("uses default value for input[type='text']", function(done) {
49+
CanvasRenderer.prototype.text = function(text) {
50+
expect(text).to.equal('text');
51+
};
52+
html2canvas(document.querySelector("#block1"), {renderer: CanvasRenderer, strict: true}).then(function(canvas) {
53+
expect(canvas.width).to.equal(200);
54+
expect(canvas.height).to.equal(200);
55+
done();
56+
}).catch(function(error) {
57+
done(error);
58+
});
59+
});
60+
61+
it("uses transformed value for input[type='password']", function(done) {
62+
var count = 0;
63+
CanvasRenderer.prototype.text = function(text) {
64+
expect(text).to.equal('•');
65+
count++;
66+
};
67+
html2canvas(document.querySelector("#block2"), {renderer: CanvasRenderer, strict: true}).then(function(canvas) {
68+
expect(canvas.width).to.equal(200);
69+
expect(canvas.height).to.equal(200);
70+
expect(count).to.equal("password".length);
71+
done();
72+
}).catch(function(error) {
73+
done(error);
74+
});
75+
});
76+
77+
it("used property and not attribute for rendering", function(done) {
78+
document.querySelector("#block3 input").value = 'updated';
79+
80+
CanvasRenderer.prototype.text = function(text) {
81+
expect(text).to.equal('updated');
82+
};
83+
html2canvas(document.querySelector("#block3"), {renderer: CanvasRenderer, strict: true}).then(function(canvas) {
84+
expect(canvas.width).to.equal(200);
85+
expect(canvas.height).to.equal(200);
86+
done();
87+
}).catch(function(error) {
88+
done(error);
89+
});
90+
});
91+
92+
describe("Rendering textarea values", function() {
93+
it("uses default value correctly", function(done) {
94+
CanvasRenderer.prototype.text = function(text) {
95+
expect(text).to.equal('text');
96+
};
97+
html2canvas(document.querySelector("#block4"), {renderer: CanvasRenderer, strict: true}).then(function(canvas) {
98+
expect(canvas.width).to.equal(200);
99+
expect(canvas.height).to.equal(200);
100+
done();
101+
}).catch(function(error) {
102+
done(error);
103+
});
104+
});
105+
106+
it("used property and not attribute for rendering", function(done) {
107+
document.querySelector("#block4 textarea").value = 'updated';
108+
109+
CanvasRenderer.prototype.text = function(text) {
110+
expect(text).to.equal('updated');
111+
};
112+
html2canvas(document.querySelector("#block4"), {renderer: CanvasRenderer, strict: true, logging: true, removeContainer: false}).then(function(canvas) {
113+
expect(canvas.width).to.equal(200);
114+
expect(canvas.height).to.equal(200);
115+
done();
116+
}).catch(function(error) {
117+
done(error);
118+
});
119+
});
120+
});
121+
122+
describe("Select values", function() {
123+
it("uses default value correctly", function(done) {
124+
CanvasRenderer.prototype.text = function(text) {
125+
expect(text).to.equal('2');
126+
};
127+
html2canvas(document.querySelector("#block5"), {renderer: CanvasRenderer, strict: true}).then(function(canvas) {
128+
expect(canvas.width).to.equal(200);
129+
expect(canvas.height).to.equal(200);
130+
done();
131+
}).catch(function(error) {
132+
done(error);
133+
});
134+
});
135+
136+
it("used property and not attribute for rendering", function(done) {
137+
document.querySelector("#block5 select").value = '3';
138+
139+
CanvasRenderer.prototype.text = function(text) {
140+
expect(text).to.equal('3');
141+
};
142+
html2canvas(document.querySelector("#block5"), {renderer: CanvasRenderer, strict: true, logging: true, removeContainer: false}).then(function(canvas) {
143+
expect(canvas.width).to.equal(200);
144+
expect(canvas.height).to.equal(200);
145+
done();
146+
}).catch(function(error) {
147+
done(error);
148+
});
149+
});
150+
});
151+
});
152+
153+
mocha.checkLeaks();
154+
mocha.globals(['jQuery']);
155+
if (window.mochaPhantomJS) {
156+
mochaPhantomJS.run();
157+
}
158+
else {
159+
mocha.run();
160+
}
161+
</script>
162+
</body>
163+
</html>

tests/mocha/password-rendering.html

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)