mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Begin implementing cross-origin iframes
This commit is contained in:
parent
3a3a61e316
commit
b141c9f0d1
@ -15,5 +15,5 @@
|
|||||||
"jQuery": true
|
"jQuery": true
|
||||||
},
|
},
|
||||||
"predef": ["NodeParser", "NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise", "getBounds", "offsetBounds", "XHR",
|
"predef": ["NodeParser", "NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise", "getBounds", "offsetBounds", "XHR",
|
||||||
"ImageContainer", "ProxyImageContainer", "DummyImageContainer", "Font", "FontMetrics", "GradientContainer", "LinearGradientContainer", "WebkitGradientContainer", "SVGContainer", "SVGNodeContainer", "FrameContainer", "html2canvas", "log", "smallImage", "parseBackgrounds"]
|
"ImageContainer", "ProxyImageContainer", "DummyImageContainer", "Font", "FontMetrics", "GradientContainer", "LinearGradientContainer", "WebkitGradientContainer", "SVGContainer", "SVGNodeContainer", "FrameContainer", "html2canvas", "log", "smallImage", "parseBackgrounds", "createWindowClone"]
|
||||||
}
|
}
|
||||||
|
31
Gruntfile.js
31
Gruntfile.js
@ -1,5 +1,6 @@
|
|||||||
/*global module:false*/
|
/*global module:false*/
|
||||||
var _ = require('lodash'), path = require('path');
|
var _ = require('lodash'), path = require('path');
|
||||||
|
var proxy = require('html2canvas-proxy');
|
||||||
|
|
||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
@ -42,21 +43,18 @@ module.exports = function(grunt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
connect: {
|
express: {
|
||||||
server: {
|
server: {
|
||||||
options: {
|
options: {
|
||||||
port: 8080,
|
port: 8080,
|
||||||
base: './',
|
bases: './'
|
||||||
keepalive: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cors: {
|
cors: {
|
||||||
options: {
|
options: {
|
||||||
port: 8081,
|
port: 8081,
|
||||||
base: './',
|
bases: './',
|
||||||
keepalive: false,
|
middleware: [
|
||||||
middleware: function(connect, options, middlwares) {
|
|
||||||
return [
|
|
||||||
function(req, res, next) {
|
function(req, res, next) {
|
||||||
if (req.url !== '/tests/assets/image2.jpg') {
|
if (req.url !== '/tests/assets/image2.jpg') {
|
||||||
next();
|
next();
|
||||||
@ -64,17 +62,20 @@ module.exports = function(grunt) {
|
|||||||
}
|
}
|
||||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
res.end(require("fs").readFileSync('tests/assets/image2.jpg'));
|
res.end(require("fs").readFileSync('tests/assets/image2.jpg'));
|
||||||
},
|
|
||||||
connect.static(options.base[0])
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
options: {
|
||||||
|
port: 8082,
|
||||||
|
middleware: [proxy()]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ci: {
|
ci: {
|
||||||
options: {
|
options: {
|
||||||
port: 8080,
|
port: 8080,
|
||||||
base: './',
|
bases: './'
|
||||||
keepalive: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -170,12 +171,12 @@ module.exports = function(grunt) {
|
|||||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||||
grunt.loadNpmTasks('grunt-contrib-qunit');
|
grunt.loadNpmTasks('grunt-contrib-qunit');
|
||||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
grunt.loadNpmTasks('grunt-express');
|
||||||
grunt.loadNpmTasks('grunt-execute');
|
grunt.loadNpmTasks('grunt-execute');
|
||||||
|
|
||||||
grunt.registerTask('server', ['connect:cors', 'connect']);
|
grunt.registerTask('server', ['express:cors', 'express:proxy', 'express:server', 'express-keepalive']);
|
||||||
grunt.registerTask('build', ['execute', 'concat', 'uglify']);
|
grunt.registerTask('build', ['execute', 'concat', 'uglify']);
|
||||||
grunt.registerTask('default', ['jshint', 'build', 'qunit']);
|
grunt.registerTask('default', ['jshint', 'build', 'qunit']);
|
||||||
grunt.registerTask('travis', ['jshint', 'build','qunit', 'connect:ci', 'connect:cors', 'webdriver']);
|
grunt.registerTask('travis', ['jshint', 'build','qunit', 'express:ci', 'express:cors', 'webdriver']);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
42
dist/html2canvas.js
vendored
42
dist/html2canvas.js
vendored
@ -593,7 +593,7 @@ window.html2canvas = function(nodeList, options) {
|
|||||||
window.html2canvas.punycode = this.punycode;
|
window.html2canvas.punycode = this.punycode;
|
||||||
|
|
||||||
function renderDocument(document, options, windowWidth, windowHeight) {
|
function renderDocument(document, options, windowWidth, windowHeight) {
|
||||||
return createWindowClone(document, windowWidth, windowHeight, options).then(function(container) {
|
return createWindowClone(document, document, windowWidth, windowHeight, options).then(function(container) {
|
||||||
log("Document cloned");
|
log("Document cloned");
|
||||||
var selector = "[" + html2canvasNodeAttribute + "='true']";
|
var selector = "[" + html2canvasNodeAttribute + "='true']";
|
||||||
document.querySelector(selector).removeAttribute(html2canvasNodeAttribute);
|
document.querySelector(selector).removeAttribute(html2canvasNodeAttribute);
|
||||||
@ -652,9 +652,9 @@ function smallImage() {
|
|||||||
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWindowClone(ownerDocument, width, height, options) {
|
function createWindowClone(ownerDocument, containerDocument, width, height, options) {
|
||||||
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
||||||
container = ownerDocument.createElement("iframe");
|
container = containerDocument.createElement("iframe");
|
||||||
|
|
||||||
container.style.visibility = "hidden";
|
container.style.visibility = "hidden";
|
||||||
container.style.position = "absolute";
|
container.style.position = "absolute";
|
||||||
@ -662,7 +662,7 @@ function createWindowClone(ownerDocument, width, height, options) {
|
|||||||
container.width = width;
|
container.width = width;
|
||||||
container.height = height;
|
container.height = height;
|
||||||
container.scrolling = "no"; // ios won't scroll without it
|
container.scrolling = "no"; // ios won't scroll without it
|
||||||
ownerDocument.body.appendChild(container);
|
containerDocument.body.appendChild(container);
|
||||||
|
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
var documentClone = container.contentWindow.document;
|
var documentClone = container.contentWindow.document;
|
||||||
@ -777,12 +777,12 @@ FontMetrics.prototype.getMetrics = function(family, size) {
|
|||||||
return this.data[family + "-" + size];
|
return this.data[family + "-" + size];
|
||||||
};
|
};
|
||||||
|
|
||||||
function FrameContainer(container) {
|
function FrameContainer(container, sameOrigin, proxy) {
|
||||||
this.image = null;
|
this.image = null;
|
||||||
this.src = container;
|
this.src = container;
|
||||||
var self = this;
|
var self = this;
|
||||||
var bounds = getBounds(container);
|
var bounds = getBounds(container);
|
||||||
this.promise = new Promise(function(resolve) {
|
this.promise = (!sameOrigin ? this.proxyLoad(proxy, bounds) : new Promise(function(resolve) {
|
||||||
if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) {
|
if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) {
|
||||||
container.contentWindow.onload = container.onload = function() {
|
container.contentWindow.onload = container.onload = function() {
|
||||||
resolve(container);
|
resolve(container);
|
||||||
@ -790,8 +790,7 @@ function FrameContainer(container) {
|
|||||||
} else {
|
} else {
|
||||||
resolve(container);
|
resolve(container);
|
||||||
}
|
}
|
||||||
|
})).then(function(container) {
|
||||||
}).then(function(container) {
|
|
||||||
return html2canvas(container.contentWindow.document.documentElement, {
|
return html2canvas(container.contentWindow.document.documentElement, {
|
||||||
width: bounds.width,
|
width: bounds.width,
|
||||||
height: bounds.height
|
height: bounds.height
|
||||||
@ -801,6 +800,31 @@ function FrameContainer(container) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FrameContainer.prototype.proxyLoad = function(proxy, bounds) {
|
||||||
|
var container = this.src;
|
||||||
|
return XHR(proxy + "?url=" + container.src).then(documentFromHTML(container)).then(function(doc) {
|
||||||
|
return createWindowClone(doc, container.ownerDocument, bounds.width, bounds.height, {});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function documentFromHTML(container) {
|
||||||
|
return function(html) {
|
||||||
|
var doc = document.implementation.createHTMLDocument("");
|
||||||
|
doc.open();
|
||||||
|
doc.write(html);
|
||||||
|
doc.close();
|
||||||
|
|
||||||
|
var b = doc.querySelector("base");
|
||||||
|
if (!b || !b.href.host) {
|
||||||
|
var base = doc.createElement("base");
|
||||||
|
base.href = container.src;
|
||||||
|
doc.head.insertBefore(base, doc.head.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function GradientContainer(imageData) {
|
function GradientContainer(imageData) {
|
||||||
this.src = imageData.value;
|
this.src = imageData.value;
|
||||||
this.colorStops = [];
|
this.colorStops = [];
|
||||||
@ -913,7 +937,7 @@ ImageLoader.prototype.loadImage = function(imageData) {
|
|||||||
} else if (imageData.method === "svg") {
|
} else if (imageData.method === "svg") {
|
||||||
return new SVGNodeContainer(imageData.args[0]);
|
return new SVGNodeContainer(imageData.args[0]);
|
||||||
} else if (imageData.method === "IFRAME") {
|
} else if (imageData.method === "IFRAME") {
|
||||||
return new FrameContainer(imageData.args[0]);
|
return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options.proxy);
|
||||||
} else {
|
} else {
|
||||||
return new DummyImageContainer(imageData);
|
return new DummyImageContainer(imageData);
|
||||||
}
|
}
|
||||||
|
4
dist/html2canvas.min.js
vendored
4
dist/html2canvas.min.js
vendored
File diff suppressed because one or more lines are too long
@ -24,12 +24,13 @@
|
|||||||
"bluebird": "^2.3.2",
|
"bluebird": "^2.3.2",
|
||||||
"grunt": "^0.4.5",
|
"grunt": "^0.4.5",
|
||||||
"grunt-contrib-concat": "*",
|
"grunt-contrib-concat": "*",
|
||||||
"grunt-contrib-connect": "0.7.1",
|
|
||||||
"grunt-contrib-jshint": "*",
|
"grunt-contrib-jshint": "*",
|
||||||
"grunt-contrib-qunit": "*",
|
"grunt-contrib-qunit": "*",
|
||||||
"grunt-contrib-uglify": "*",
|
"grunt-contrib-uglify": "*",
|
||||||
"grunt-contrib-watch": "~0.5.1",
|
"grunt-contrib-watch": "~0.5.1",
|
||||||
"grunt-execute": "^0.2.2",
|
"grunt-execute": "^0.2.2",
|
||||||
|
"grunt-express": "^1.4.1",
|
||||||
|
"html2canvas-proxy": "0.0.2",
|
||||||
"humanize-duration": "^2.0.1",
|
"humanize-duration": "^2.0.1",
|
||||||
"lodash": "^2.4.1",
|
"lodash": "^2.4.1",
|
||||||
"png-js": ">= 0.1.1",
|
"png-js": ">= 0.1.1",
|
||||||
|
@ -25,7 +25,7 @@ window.html2canvas = function(nodeList, options) {
|
|||||||
window.html2canvas.punycode = this.punycode;
|
window.html2canvas.punycode = this.punycode;
|
||||||
|
|
||||||
function renderDocument(document, options, windowWidth, windowHeight) {
|
function renderDocument(document, options, windowWidth, windowHeight) {
|
||||||
return createWindowClone(document, windowWidth, windowHeight, options).then(function(container) {
|
return createWindowClone(document, document, windowWidth, windowHeight, options).then(function(container) {
|
||||||
log("Document cloned");
|
log("Document cloned");
|
||||||
var selector = "[" + html2canvasNodeAttribute + "='true']";
|
var selector = "[" + html2canvasNodeAttribute + "='true']";
|
||||||
document.querySelector(selector).removeAttribute(html2canvasNodeAttribute);
|
document.querySelector(selector).removeAttribute(html2canvasNodeAttribute);
|
||||||
@ -84,9 +84,9 @@ function smallImage() {
|
|||||||
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWindowClone(ownerDocument, width, height, options) {
|
function createWindowClone(ownerDocument, containerDocument, width, height, options) {
|
||||||
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
||||||
container = ownerDocument.createElement("iframe");
|
container = containerDocument.createElement("iframe");
|
||||||
|
|
||||||
container.style.visibility = "hidden";
|
container.style.visibility = "hidden";
|
||||||
container.style.position = "absolute";
|
container.style.position = "absolute";
|
||||||
@ -94,7 +94,7 @@ function createWindowClone(ownerDocument, width, height, options) {
|
|||||||
container.width = width;
|
container.width = width;
|
||||||
container.height = height;
|
container.height = height;
|
||||||
container.scrolling = "no"; // ios won't scroll without it
|
container.scrolling = "no"; // ios won't scroll without it
|
||||||
ownerDocument.body.appendChild(container);
|
containerDocument.body.appendChild(container);
|
||||||
|
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
var documentClone = container.contentWindow.document;
|
var documentClone = container.contentWindow.document;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
function FrameContainer(container) {
|
function FrameContainer(container, sameOrigin, proxy) {
|
||||||
this.image = null;
|
this.image = null;
|
||||||
this.src = container;
|
this.src = container;
|
||||||
var self = this;
|
var self = this;
|
||||||
var bounds = getBounds(container);
|
var bounds = getBounds(container);
|
||||||
this.promise = new Promise(function(resolve) {
|
this.promise = (!sameOrigin ? this.proxyLoad(proxy, bounds) : new Promise(function(resolve) {
|
||||||
if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) {
|
if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) {
|
||||||
container.contentWindow.onload = container.onload = function() {
|
container.contentWindow.onload = container.onload = function() {
|
||||||
resolve(container);
|
resolve(container);
|
||||||
@ -11,8 +11,7 @@ function FrameContainer(container) {
|
|||||||
} else {
|
} else {
|
||||||
resolve(container);
|
resolve(container);
|
||||||
}
|
}
|
||||||
|
})).then(function(container) {
|
||||||
}).then(function(container) {
|
|
||||||
return html2canvas(container.contentWindow.document.documentElement, {
|
return html2canvas(container.contentWindow.document.documentElement, {
|
||||||
width: bounds.width,
|
width: bounds.width,
|
||||||
height: bounds.height
|
height: bounds.height
|
||||||
@ -21,3 +20,28 @@ function FrameContainer(container) {
|
|||||||
return self.image = canvas;
|
return self.image = canvas;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FrameContainer.prototype.proxyLoad = function(proxy, bounds) {
|
||||||
|
var container = this.src;
|
||||||
|
return XHR(proxy + "?url=" + container.src).then(documentFromHTML(container)).then(function(doc) {
|
||||||
|
return createWindowClone(doc, container.ownerDocument, bounds.width, bounds.height, {});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function documentFromHTML(container) {
|
||||||
|
return function(html) {
|
||||||
|
var doc = document.implementation.createHTMLDocument("");
|
||||||
|
doc.open();
|
||||||
|
doc.write(html);
|
||||||
|
doc.close();
|
||||||
|
|
||||||
|
var b = doc.querySelector("base");
|
||||||
|
if (!b || !b.href.host) {
|
||||||
|
var base = doc.createElement("base");
|
||||||
|
base.href = container.src;
|
||||||
|
doc.head.insertBefore(base, doc.head.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -69,7 +69,7 @@ ImageLoader.prototype.loadImage = function(imageData) {
|
|||||||
} else if (imageData.method === "svg") {
|
} else if (imageData.method === "svg") {
|
||||||
return new SVGNodeContainer(imageData.args[0]);
|
return new SVGNodeContainer(imageData.args[0]);
|
||||||
} else if (imageData.method === "IFRAME") {
|
} else if (imageData.method === "IFRAME") {
|
||||||
return new FrameContainer(imageData.args[0]);
|
return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options.proxy);
|
||||||
} else {
|
} else {
|
||||||
return new DummyImageContainer(imageData);
|
return new DummyImageContainer(imageData);
|
||||||
}
|
}
|
||||||
|
13
tests/cases/crossorigin-iframe.html
Normal file
13
tests/cases/crossorigin-iframe.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>cross-origin iframe test</title>
|
||||||
|
<script>
|
||||||
|
var h2cOptions = {proxy: "http://localhost:8082"};
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="../test.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe src="http://hertzen.com/" width="500" height="500"></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user