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
|
||||
},
|
||||
"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"]
|
||||
}
|
||||
|
47
Gruntfile.js
47
Gruntfile.js
@ -1,5 +1,6 @@
|
||||
/*global module:false*/
|
||||
var _ = require('lodash'), path = require('path');
|
||||
var proxy = require('html2canvas-proxy');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
@ -42,39 +43,39 @@ module.exports = function(grunt) {
|
||||
}
|
||||
}
|
||||
},
|
||||
connect: {
|
||||
express: {
|
||||
server: {
|
||||
options: {
|
||||
port: 8080,
|
||||
base: './',
|
||||
keepalive: true
|
||||
bases: './'
|
||||
}
|
||||
},
|
||||
cors: {
|
||||
options: {
|
||||
port: 8081,
|
||||
base: './',
|
||||
keepalive: false,
|
||||
middleware: function(connect, options, middlwares) {
|
||||
return [
|
||||
function(req, res, next) {
|
||||
if (req.url !== '/tests/assets/image2.jpg') {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.end(require("fs").readFileSync('tests/assets/image2.jpg'));
|
||||
},
|
||||
connect.static(options.base[0])
|
||||
];
|
||||
}
|
||||
bases: './',
|
||||
middleware: [
|
||||
function(req, res, next) {
|
||||
if (req.url !== '/tests/assets/image2.jpg') {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.end(require("fs").readFileSync('tests/assets/image2.jpg'));
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
proxy: {
|
||||
options: {
|
||||
port: 8082,
|
||||
middleware: [proxy()]
|
||||
}
|
||||
},
|
||||
ci: {
|
||||
options: {
|
||||
port: 8080,
|
||||
base: './',
|
||||
keepalive: false
|
||||
bases: './'
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -170,12 +171,12 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-qunit');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-express');
|
||||
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('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;
|
||||
|
||||
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");
|
||||
var selector = "[" + html2canvasNodeAttribute + "='true']";
|
||||
document.querySelector(selector).removeAttribute(html2canvasNodeAttribute);
|
||||
@ -652,9 +652,9 @@ function smallImage() {
|
||||
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),
|
||||
container = ownerDocument.createElement("iframe");
|
||||
container = containerDocument.createElement("iframe");
|
||||
|
||||
container.style.visibility = "hidden";
|
||||
container.style.position = "absolute";
|
||||
@ -662,7 +662,7 @@ function createWindowClone(ownerDocument, width, height, options) {
|
||||
container.width = width;
|
||||
container.height = height;
|
||||
container.scrolling = "no"; // ios won't scroll without it
|
||||
ownerDocument.body.appendChild(container);
|
||||
containerDocument.body.appendChild(container);
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
var documentClone = container.contentWindow.document;
|
||||
@ -777,12 +777,12 @@ FontMetrics.prototype.getMetrics = function(family, size) {
|
||||
return this.data[family + "-" + size];
|
||||
};
|
||||
|
||||
function FrameContainer(container) {
|
||||
function FrameContainer(container, sameOrigin, proxy) {
|
||||
this.image = null;
|
||||
this.src = container;
|
||||
var self = this;
|
||||
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) {
|
||||
container.contentWindow.onload = container.onload = function() {
|
||||
resolve(container);
|
||||
@ -790,8 +790,7 @@ function FrameContainer(container) {
|
||||
} else {
|
||||
resolve(container);
|
||||
}
|
||||
|
||||
}).then(function(container) {
|
||||
})).then(function(container) {
|
||||
return html2canvas(container.contentWindow.document.documentElement, {
|
||||
width: bounds.width,
|
||||
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) {
|
||||
this.src = imageData.value;
|
||||
this.colorStops = [];
|
||||
@ -913,7 +937,7 @@ ImageLoader.prototype.loadImage = function(imageData) {
|
||||
} else if (imageData.method === "svg") {
|
||||
return new SVGNodeContainer(imageData.args[0]);
|
||||
} 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 {
|
||||
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",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-contrib-concat": "*",
|
||||
"grunt-contrib-connect": "0.7.1",
|
||||
"grunt-contrib-jshint": "*",
|
||||
"grunt-contrib-qunit": "*",
|
||||
"grunt-contrib-uglify": "*",
|
||||
"grunt-contrib-watch": "~0.5.1",
|
||||
"grunt-execute": "^0.2.2",
|
||||
"grunt-express": "^1.4.1",
|
||||
"html2canvas-proxy": "0.0.2",
|
||||
"humanize-duration": "^2.0.1",
|
||||
"lodash": "^2.4.1",
|
||||
"png-js": ">= 0.1.1",
|
||||
|
@ -25,7 +25,7 @@ window.html2canvas = function(nodeList, options) {
|
||||
window.html2canvas.punycode = this.punycode;
|
||||
|
||||
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");
|
||||
var selector = "[" + html2canvasNodeAttribute + "='true']";
|
||||
document.querySelector(selector).removeAttribute(html2canvasNodeAttribute);
|
||||
@ -84,9 +84,9 @@ function smallImage() {
|
||||
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),
|
||||
container = ownerDocument.createElement("iframe");
|
||||
container = containerDocument.createElement("iframe");
|
||||
|
||||
container.style.visibility = "hidden";
|
||||
container.style.position = "absolute";
|
||||
@ -94,7 +94,7 @@ function createWindowClone(ownerDocument, width, height, options) {
|
||||
container.width = width;
|
||||
container.height = height;
|
||||
container.scrolling = "no"; // ios won't scroll without it
|
||||
ownerDocument.body.appendChild(container);
|
||||
containerDocument.body.appendChild(container);
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
var documentClone = container.contentWindow.document;
|
||||
|
@ -1,9 +1,9 @@
|
||||
function FrameContainer(container) {
|
||||
function FrameContainer(container, sameOrigin, proxy) {
|
||||
this.image = null;
|
||||
this.src = container;
|
||||
var self = this;
|
||||
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) {
|
||||
container.contentWindow.onload = container.onload = function() {
|
||||
resolve(container);
|
||||
@ -11,8 +11,7 @@ function FrameContainer(container) {
|
||||
} else {
|
||||
resolve(container);
|
||||
}
|
||||
|
||||
}).then(function(container) {
|
||||
})).then(function(container) {
|
||||
return html2canvas(container.contentWindow.document.documentElement, {
|
||||
width: bounds.width,
|
||||
height: bounds.height
|
||||
@ -21,3 +20,28 @@ function FrameContainer(container) {
|
||||
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") {
|
||||
return new SVGNodeContainer(imageData.args[0]);
|
||||
} 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 {
|
||||
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