Skip to content
This repository has been archived by the owner on Oct 2, 2018. It is now read-only.

Commit

Permalink
Sandbox documents to be printed
Browse files Browse the repository at this point in the history
Currently scripts in documents are allowed, so we need to sandbox them
so they can't go and read other documents.

We can't put the document in a sandboxed iframe because that breaks
clean pagination and reflow. Currently, meta sandbox is poorly
supported, as are Service Workers (which would also be a heavyweight
solution).

So we open the window from a sandboxed iframe, because no-allow-same-
origin trickles down to the opened window. We then position that iframe
over the print button, because of popup blockers.

This commit also fixes printing of non-html files.
  • Loading branch information
twiss committed Feb 4, 2015
1 parent 71a4261 commit 4a5c205
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 102 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ <h2 data-l10n-id="document-actions">Document actions</h2>
<li><a href="#" data-click="nav" data-click-location="add" data-l10n-id="create-document">Create document</a></li>
<li><a href="#" data-click="nav" data-click-location="open" data-l10n-id="open-document">Open document</a></li>
<li><a href="#" data-click="saveFromEditor" data-l10n-id="save-document">Save document</a></li>
<li><a href="#" data-click="printFile" data-l10n-id="print-document">Print document</a></li>
<li><a href="#" data-click="printFile" data-l10n-id="print-document">Print document</a><iframe class="printButton" id="printButton" sandbox="allow-scripts allow-popups" src=""></iframe></li>
<li><a href="#" data-click="closeFile" data-l10n-id="close-document">Close document</a></li>
</ul>
<h2 data-l10n-id="help">Help</h2>
Expand Down
7 changes: 7 additions & 0 deletions modules/editor/scripts/docIO.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ function initDocIO(doc, messageProxy, loadCallback) {
});
}, "get-content-blob");

messageProxy.registerMessageHandler(function(e) {
messageProxy.getPort().postMessage({
command: e.data.key,
content: doc.innerHTML
});
}, "get-content-html");

messageProxy.registerMessageHandler(function(e) {
load(e.data.content, e.data.filetype);
if(e.data.key) {
Expand Down
25 changes: 25 additions & 0 deletions modules/printButton/printButton.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
* Firetext Editor
* Copyright (C) Codexa Organization.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">

<!-- Lib -->
<script type="text/javascript" src="../../scripts/lib/kamino.js"></script>
<script type="text/javascript" src="../../scripts/lib/message_channel.js"></script>

<!-- Message Communication Proxy -->
<script type="text/javascript" src="../../scripts/messages.js"></script>

<!-- Main Script -->
<script type="text/javascript" src="scripts/printButton.js"></script>

<!-- Styles -->
<link rel="stylesheet" type="text/css" href="style/printButton.css"/>
</head>
<body>
</body>
</html>
107 changes: 107 additions & 0 deletions modules/printButton/scripts/printButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
window.addEventListener('DOMContentLoaded', function() {
// WARNING: DO NOT REPLACE, THIS STRING IS REPLACED WITH THE ORIGIN AUTOMATICALLY WHEN LOADED FROM editorProxy.js
var mainOrigin = "[ORIGIN_OF_MAIN_DOCUMENT]";

// Proxy for communication with parent page
var parentMessageProxy;

window.addEventListener("message", function(e){
if(e.origin !== mainOrigin) {
throw new Error("origin did not match");
}
if(e.data.command === "init" && e.ports.length) {
// register port
parentMessageProxy = new MessageProxy(e.ports[0]);
parentMessageProxy.getPort().start();
}
}, false);

window.addEventListener('click', function() {
var win = window.open(URL.createObjectURL(new Blob([
"<script>",
"window.addEventListener('message', function(evt) {",
" document.open('text/html', 'replace');",
" document.write(evt.data.content);",
" document.close();",
"});",
"</script>",
], {type: 'text/html'})));
var key = parentMessageProxy.registerMessageHandler(function(e){
win.postMessage({
content: [
'<!DOCTYPE html>',
'<html moznomarginboxes>',
'<head>',
' <meta charset="utf-8">',
' <title>' + e.data.filename.replace(/</g, '&lt;') + e.data.filetype + '</title>',
' <style>',
' #firetext_print_notice {',
' border: 2px solid;',
' font-size: xx-large;',
' margin: 20px;',
' padding: 20px;',
' border-radius: 8px;',
' font-family: sans-serif;',
' }',
' @media print {',
' #firetext_print_notice {',
' display: none;',
' }',
' }',
' </style>',
'</head>',
'<body>',
'',
e.data.content,
"",
"<script>",
"var onAfterPrint = function(mql) {",
" if(!mql.matches) {",
" window.close();",
" }",
"};",
"var mql = window.matchMedia('print');",
"if(",
" navigator.userAgent.indexOf('Chrome') !== -1 &&",
" // In both Firefox and IE the window.print() dialog is less featureful than menu -> print,",
" // (preview and page settings,) so we allow the user to select the latter if they want.",
" // Also, they don't support the following (they have onafterprint), but they might in the future.",
" navigator.userAgent.indexOf('Android') === -1",
" // Chrome Android returns from printing before it's done and crashes if we window.close().",
" // Also match Android tablet and WebView Android because they're probably the same.",
") {",
" mql.addListener(onAfterPrint);",
"}",
"var onAutoPrintUnsupported = function() {",
" // The browser doesn't support window.print() (or window.print() is non-blocking).",
" // So we ask the user that, if the browser supports printing, they print manually.",
" var notice = document.createElement('div');",
" notice.id = 'firetext_print_notice';",
" notice.textContent = " + JSON.stringify(e.data['automatic-printing-failed']) + ";",
" document.body.insertBefore(notice, document.body.firstChild);",
" mql.removeListener(onAfterPrint);",
" onAutoPrintUnsupported = function() {};",
"};",
"var t0 = Date.now();",
"setTimeout(function() {",
" if(Date.now() - t0 < 200) {",
" onAutoPrintUnsupported();",
" }",
"});",
"try {",
" window.print();",
"} catch(e) {",
" onAutoPrintUnsupported();",
"}",
"</script>",
"</body>",
"</html>",
].join('\n')
}, '*');
}, null, true);
parentMessageProxy.getPort().postMessage({
command: "print-button-pressed",
key: key
});
});
});
4 changes: 4 additions & 0 deletions modules/printButton/style/printButton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
html {
height: 100%;
cursor: pointer;
}
149 changes: 48 additions & 101 deletions scripts/firetext.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ firetext.initialized = new CustomEvent('firetext.initialized');
firetext.isInitialized = false;
var html = document.getElementsByTagName('html')[0], head = document.getElementsByTagName("head")[0];
var themeColor = document.getElementById("theme-color");
var loadSpinner, editor, toolbar, toolbarInterval, editWindow, editState, rawEditor, tabRaw, tabDesign;
var loadSpinner, editor, toolbar, toolbarInterval, editWindow, editState, rawEditor, tabRaw, tabDesign, printButton;
var deviceType, fileChanged, saveTimeout, saving, tempAutozen, urls={}, version = '0.4';
var bold, boldCheckbox, italic, italicCheckbox, justifySelect, strikethrough, strikethroughCheckbox;
var underline, underlineCheckbox;
Expand Down Expand Up @@ -128,6 +128,11 @@ function initModules(callback) {
firetext.io.init(null, function() {
callback();
});

// Initialize print button
initPrintButton(function() {

});
}

function initElements() {
Expand All @@ -142,6 +147,7 @@ function initElements() {
editWindow = document.getElementById('edit');
locationLegend = document.getElementById('locationLegend');
locationSelect = document.getElementById('createDialogFileLocation');
printButton = document.getElementById('printButton');

// Lists
welcomeDocsList = document.getElementById('welcome-docs-list');
Expand Down Expand Up @@ -1064,106 +1070,6 @@ function processActions(eventAttribute, target) {
regions.sidebar(target.getAttribute(eventAttribute + '-id'), target.getAttribute(eventAttribute + '-state'));
} else if (calledFunction == 'saveFromEditor') {
saveFromEditor(true, true);
} else if (calledFunction == 'printFile') {
var filename = document.getElementById('currentFileName').textContent;
var filetype = document.getElementById('currentFileType').textContent;

var win = window.open(URL.createObjectURL(new Blob([
"<script>",
"window.addEventListener('message', function(evt) {",
" document.open(evt.data.type, 'replace');",
" document.write(evt.data.content);",
" document.close();",
"});",
"</script>",
], {type: 'text/html'})));
var key = editorMessageProxy.registerMessageHandler(function(e){
var reader = new FileReader();
reader.addEventListener('load', function() {
win.postMessage({
content: reader.result,
type: e.data.type
}, '*');
});
reader.readAsText(new Blob([
[
'<!DOCTYPE html>',
'<html moznomarginboxes>',
'<head>',
' <meta charset="utf-8">',
' <title>' + filename.replace(/</g, '&lt;') + filetype + '</title>',
' <style>',
' #firetext_print_notice {',
' border: 2px solid;',
' font-size: xx-large;',
' margin: 20px;',
' padding: 20px;',
' border-radius: 8px;',
' font-family: sans-serif;',
' }',
' @media print {',
' #firetext_print_notice {',
' display: none;',
' }',
' }',
' </style>',
'</head>',
'<body>',
''
].join('\n'),
StringView.base64ToBytes(e.data.content),
[
"",
"<script>",
"var onAfterPrint = function(mql) {",
" if(!mql.matches) {",
" window.close();",
" }",
"};",
"var mql = window.matchMedia('print');",
"if(",
" navigator.userAgent.indexOf('Chrome') !== -1 &&",
" // In both Firefox and IE the window.print() dialog is less featureful than menu -> print,",
" // (preview and page settings,) so we allow the user to select the latter if they want.",
" // Also, they don't support the following (they have onafterprint), but they might in the future.",
" navigator.userAgent.indexOf('Android') === -1",
" // Chrome Android returns from printing before it's done and crashes if we window.close().",
" // Also match Android tablet and WebView Android because they're probably the same.",
") {",
" mql.addListener(onAfterPrint);",
"}",
"var onAutoPrintUnsupported = function() {",
" // The browser doesn't support window.print() (or window.print() is non-blocking).",
" // So we ask the user that, if the browser supports printing, they print manually.",
" var notice = document.createElement('div');",
" notice.id = 'firetext_print_notice';",
" notice.textContent = " + JSON.stringify(navigator.mozL10n.get('automatic-printing-failed')) + ";",
" document.body.insertBefore(notice, document.body.firstChild);",
" mql.removeListener(onAfterPrint);",
" onAutoPrintUnsupported = function() {};",
"};",
"var t0 = Date.now();",
"setTimeout(function() {",
" if(Date.now() - t0 < 200) {",
" onAutoPrintUnsupported();",
" }",
"});",
"try {",
" window.print();",
"} catch(e) {",
" onAutoPrintUnsupported();",
"}",
"</script>",
"</body>",
"</html>",
].join('\n')
], {type: e.data.type}));
}, null, true);
editorMessageProxy.getPort().postMessage({
command: "get-content-blob",
key: key
});
regions.nav('edit');
} else if (calledFunction == 'closeFile') {
// Check if file is changed. If so, prompt the user to save it.
if (firetext.settings.get('autosave') == 'false' && fileChanged == true) {
Expand Down Expand Up @@ -1496,3 +1402,44 @@ document.addEventListener('webkitfullscreenerror', onFullScreenError);
firetext.alert = function(message) {
alert(message);
};

/* Print button
------------------------*/
function initPrintButton(callback) {
app.modules.load('modules/printButton/printButton.html', printButton, function() {
printButtonCommunication(function(){
callback();
});
}, true, true);
}

function printButtonCommunication(callback) {
printButton.onload = null;
printButton.onload = function() {
var printButtonMessageChannel = new MessageChannel();
// See: scripts/messages.js
var printButtonMessageProxy = new MessageProxy(printButtonMessageChannel.port1);

printButtonMessageProxy.registerMessageHandler(function(printEvt) {
var key = editorMessageProxy.registerMessageHandler(function(editorEvt){
var filename = document.getElementById('currentFileName').textContent;
var filetype = document.getElementById('currentFileType').textContent;

printButtonMessageProxy.getPort().postMessage({
command: printEvt.data.key,
filename: filename,
filetype: filetype,
content: editorEvt.data.content,
'automatic-printing-failed': navigator.mozL10n.get('automatic-printing-failed')
});
}, null, true);
editorMessageProxy.getPort().postMessage({
command: "get-content-html",
key: key
});
regions.nav('edit');
}, "print-button-pressed");
Window.postMessage(printButton.contentWindow, {command: "init"}, "*", [printButtonMessageChannel.port2]);
printButtonMessageProxy.getPort().start();
}
}
11 changes: 11 additions & 0 deletions style/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ section > header:first-child .icon.icon-close {
overflow: hidden;
}

section[data-type="sidebar"] > nav > ul > li {
position: relative;
}
section[data-type="sidebar"] > nav > ul > li > iframe {
border: 0;
position: absolute;
top: 0;
width: 100%;
height: 100%;
}


/* action_menu.css */
form[role=dialog] {
Expand Down

0 comments on commit 4a5c205

Please sign in to comment.