Bladeren bron

fixed implementation of script injection in frame contents

Gildas 7 jaren geleden
bovenliggende
commit
b6cee28b75

+ 3 - 3
.gitignore

@@ -1,4 +1,4 @@
-/node_modules/
 /.vscode/
-/cli/chromedriver.exe
-/cli/geckodriver.exe
+**/node_modules/
+**/chromedriver.exe
+**/geckodriver.exe

+ 2 - 1
README.MD

@@ -53,7 +53,8 @@ SingleFile can be installed on:
 See https://addons.mozilla.org/en-US/firefox/addon/single-file/statistics/?last=90
 
 ## Command Line Interface
-You can save web pages or convert MAFF files to HTML from the command line interface. See here for more info: https://github.com/gildas-lormeau/SingleFile/blob/master/cli/README.MD.
+ - You can save web pages to HTML from the command line interface. See here for more info: https://github.com/gildas-lormeau/SingleFile/blob/master/cli/README.MD.
+ - You can also convert MAFF pages to HTML. For this, you must use specific versions of SingleFile CLI and Firefox. See here for more info: https://github.com/gildas-lormeau/SingleFile/blob/master/maff2html/README.MD.
 
 ## Known Issues
 - All browsers:

+ 0 - 7
cli/README.MD

@@ -15,7 +15,6 @@ This is a **work in progress**.
 - With Selenium WebDriver, you can use [Firefox](https://www.mozilla.org/en/firefox/new/) or Chrome/Chromium. At least one of them must be installed. 
 - If you use [Selenium WebDriver](https://www.npmjs.com/package/selenium-webdriver) instead of Puppeteer, you only need to download the component (e.g. `geckodriver` or `chromedriver`) for your browser and ensure it can be found through the `PATH` environment variable or the current folder. Otherwise you will need to set the `--web-driver-executable-path` option to help SingleFile locating the binary file.
 - It is recommended that the browser binaries can be found through the `PATH` environment variable or the current folder. Otherwise you will need to set the `--browser-executable-path` option to help SingleFile locating the binary file.
-- To convert MAFF files to HTML, you must use specific versions of Firefox and Selenium WebDriver component. You must install respectively [Firefox version 56.0.2](https://ftp.mozilla.org/pub/firefox/releases/56.0.2/) and [Mozilla geckodriver version 0.20.1](https://github.com/mozilla/geckodriver/releases/tag/v0.20.1). See the last example for more info about the options to set.
 
 ### Install
 
@@ -68,12 +67,6 @@ This is a **work in progress**.
   `$ ./single-file.js https://www.wikipedia.org wikipedia.html --back-end=jsdom` (Linux/Unix/BSD etc.)
   
   `> node single-file.js https://www.wikipedia.org wikipedia.html --back-end=jsdom` (Windows)
-
-  - Convert a MAFF file (e.g. `wikipedia.maff`) into `wikipedia.html` with Firefox installed in the folder "~/firefox-56.0.2/" (Linux/Unix/BSD etc.) or "C:\Program Files\Mozilla Firefox 56.0.2\" (Windows)
-
-  `$ ./single-file.js file:///home/gildas/wikipedia.maff wikipedia.html --enable-MAFF  --load-deferred-images=false --back-end=webdriver-gecko --browser-executable-path=~/firefox-56.0.2/firefox` (Linux/Unix/BSD etc.)
-  
-  `> node single-file.js file:///C:/Users/Gildas/wikipedia.maff wikipedia.html --enable-MAFF  --load-deferred-images=false --back-end=webdriver-gecko --browser-executable-path="C:\Program Files\Mozilla Firefox 56.0.2\firefox"` (Windows)
   
 ## License
 

+ 0 - 11
cli/back-ends/webdriver-chromium.js

@@ -111,14 +111,6 @@ exports.getPageData = async options => {
 		} else {
 			await driver.executeScript(scripts);
 		}
-		if (!options.removeFrames) {
-			const windowHandles = await driver.getAllWindowHandles();
-			await Promise.all(windowHandles.map(async windowHandle => {
-				await driver.switchTo().window(windowHandle);
-				driver.executeScript(scripts);
-			}));
-			await driver.switchTo().window(driver.getWindowHandle());
-		}
 		const result = await driver.executeAsyncScript(getPageDataScript(), options);
 		if (result.error) {
 			throw result.error;
@@ -143,9 +135,6 @@ function getPageDataScript() {
 		options.insertSingleFileComment = true;
 		const preInitializationPromises = [];
 		if (!options.saveRawPage) {
-			if (!options.removeFrames) {
-				preInitializationPromises.push(frameTree.getAsync(options));
-			}
 			if (options.loadDeferredImages) {
 				preInitializationPromises.push(lazyLoader.process(options));
 			}

+ 26 - 15
cli/back-ends/webdriver-gecko.js

@@ -70,23 +70,22 @@ exports.getPageData = async options => {
 		if (options.webDriverExecutablePath) {
 			process.env["webdriver.gecko.driver"] = options.webDriverExecutablePath;
 		}
-		const profile = new firefox.Profile();
+		const extensions = [];
 		if (options.browserDisableWebSecurity === undefined || options.browserDisableWebSecurity) {
-			profile.addExtension(require.resolve("./extensions/signed/disable_web_security-0.0.3-an+fx.xpi"));
+			extensions.push(require.resolve("./extensions/signed/disable_web_security-0.0.3-an+fx.xpi"));
 		}
 		if (options.browserBypassCSP === undefined || options.browserBypassCSP) {
-			profile.addExtension(require.resolve("./extensions/signed/bypass_csp-0.0.3-an+fx.xpi"));
+			extensions.push(require.resolve("./extensions/signed/bypass_csp-0.0.3-an+fx.xpi"));
 		}
 		if (options.browserWaitUntil === undefined || options.browserWaitUntil == "networkidle0" || options.browserWaitUntil == "networkidle2") {
-			profile.addExtension(require.resolve("./extensions/signed/network_idle-0.0.2-an+fx.xpi"));
+			extensions.push(require.resolve("./extensions/signed/network_idle-0.0.2-an+fx.xpi"));
 		}
-		if (options.userAgent) {
-			profile.setPreference("general.useragent.override", options.userAgent);
+		if (extensions.length) {
+			firefoxOptions.addExtensions(extensions);
 		}
-		if (options.enableMaff) {
-			profile.addExtension(require.resolve("./extensions/signed/mozilla_archive_format_with_mht_and_faithful_save-5.2.1-fx+sm.xpi"));
+		if (options.userAgent) {
+			firefoxOptions.setPreference("general.useragent.override", options.userAgent);
 		}
-		firefoxOptions.setProfile(profile);
 		builder.setFirefoxOptions(firefoxOptions);
 		driver = await builder.forBrowser("firefox").build();
 		driver.manage().setTimeouts({ script: null, pageLoad: null, implicit: null });
@@ -111,12 +110,7 @@ exports.getPageData = async options => {
 			await driver.executeScript(scripts);
 		}
 		if (!options.removeFrames) {
-			const windowHandles = await driver.getAllWindowHandles();
-			await Promise.all(windowHandles.map(async windowHandle => {
-				await driver.switchTo().window(windowHandle);
-				driver.executeScript(scripts);
-			}));
-			await driver.switchTo().window(driver.getWindowHandle());
+			await executeScriptInFrames(driver, scripts);
 		}
 		const result = await driver.executeAsyncScript(getPageDataScript(), options);
 		if (result.error) {
@@ -131,6 +125,23 @@ exports.getPageData = async options => {
 	}
 };
 
+async function executeScriptInFrames(driver, scripts) {
+	let finished = false, indexFrame = 0;
+	while (!finished) {
+		try {
+			await driver.switchTo().frame(indexFrame);
+		} catch (error) {
+			finished = true;
+		}
+		if (!finished) {
+			await driver.executeScript(scripts);
+			await executeScriptInFrames(driver, scripts);
+			indexFrame++;
+			await driver.switchTo().parentFrame();
+		}
+	}
+}
+
 function getPageDataScript() {
 	return `
 	const [options, callback] = arguments;

+ 3 - 2
cli/package.json

@@ -9,8 +9,9 @@
 		"jsdom": "*",
 		"puppeteer-core": "*",
 		"request-promise-native": "*",
-		"selenium-webdriver": "*",
+		"selenium-webdriver": "4.0.0-alpha.1",
+		"selenium-webdriver-legacy": "selenium-webdriver:*",
 		"strong-data-uri": "*",
 		"yargs": "*"
 	}
-}
+}

+ 0 - 3
cli/single-file.js

@@ -40,7 +40,6 @@ const args = require("yargs")
 		"browser-wait-until": "networkidle0",
 		"compress-CSS": true,
 		"compress-HTML": true,
-		"enable-MAFF": false,
 		"filename-template": "",
 		"group-duplicate-images": true,
 		"load-deferred-images": true,
@@ -73,8 +72,6 @@ const args = require("yargs")
 	.number("browser-height")
 	.options("browser-wait-until", { description: "When to consider the page is loaded (puppeteer, webdriver-gecko, webdriver-chromium)" })
 	.choices("browser-wait-until", ["networkidle0", "networkidle2", "load", "domcontentloaded"])
-	.options("enable-MAFF", { description: "Enables support of MAFF pages with Firefox < 57 (webdriver-gecko)" })
-	.boolean("enable-MAFF")
 	.options("compress-CSS", { description: "Compress CSS stylesheets" })
 	.boolean("compress-CSS")
 	.options("compress-HTML", { description: "Compress HTML content" })

File diff suppressed because it is too large
+ 4 - 0
cli/test.html


+ 58 - 0
maff2html/README.MD

@@ -0,0 +1,58 @@
+# SingleFile (MAFF to HTML)
+
+## Introduction
+
+SingleFile can be launched from the command line to convert MAFF files into HTML files by running it into Firefox. It runs through Node.js as a standalone script injected into the web page instead of being embedded into a WebExtension. To connect to Firefox, it uses [Selenium WebDriver](https://www.npmjs.com/package/selenium-webdriver).
+
+## Convert MAFF to HTML
+
+### Prerequisites
+
+- [Node.js](https://nodejs.org) must be installed. 
+- You must use specific versions of Firefox and Selenium WebDriver component. You must install respectively [Firefox version 56.0.2](https://ftp.mozilla.org/pub/firefox/releases/56.0.2/) and [Mozilla geckodriver version 0.20.1](https://github.com/mozilla/geckodriver/releases/tag/v0.20.1).
+- It is recommended that the Firefox binary can be found through the `PATH` environment variable or the current folder. Otherwise you will need to set the `--browser-executable-path` option..
+- It is also recommended that the Mozilla geckodriver binary can be found through the `PATH` environment variable or the current folder. Otherwise you will need to set the `--web-driver-executable-path` option.
+
+### Install
+
+- Unzip the [master archive](https://github.com/gildas-lormeau/SingleFile/archive/master.zip) somewhere on your disk in an empty folder.
+
+  `$ unzip master.zip .`
+  
+- Go into the `maff2html` directory.
+
+  `$ cd maff2html`
+  
+- Install dependencies with npm (installed with Node.js).
+
+  `$ npm install`
+  
+- Make `single-file.js` executable (Linux/Unix/BSD etc.).
+
+  `$ chmod +x single-file.js`
+
+### Run
+
+- Syntax
+ 
+  `$ ./single-file.js <url> [output] [options ...]` (Linux/Unix/BSD etc.)
+
+  `> node single-file.js <url> [output] [options ...]` (Windows)    
+
+- Display help
+
+  `$ ./single-file.js --help` (Linux/Unix/BSD etc.)
+
+  `> node single-file.js --help` (Windows)
+
+- Examples
+
+  - Convert a MAFF file (e.g. `wikipedia.maff`) into `wikipedia.html` with Firefox installed in the folder "~/firefox-56.0.2/" (Linux/Unix/BSD etc.) or "C:\Program Files\Mozilla Firefox 56.0.2\" (Windows)
+
+  `$ ./single-file.js file:///home/gildas/wikipedia.maff wikipedia.html --browser-executable-path=~/firefox-56.0.2/firefox` (Linux/Unix/BSD etc.)
+  
+  `> node single-file.js file:///C:/Users/Gildas/wikipedia.maff wikipedia.html --browser-executable-path="C:\Program Files\Mozilla Firefox 56.0.2\firefox"` (Windows)
+  
+## License
+
+SingleFile is licensed under AGPL and GPL. Code derived from third-party projects is licensed under MIT. Please contact me at gildas.lormeau &lt;at&gt; gmail.com if you are interested in licensing the SingleFile code for a commercial service or product.

BIN
maff2html/back-ends/extensions/signed/bypass_csp-0.0.3-an+fx.xpi


BIN
maff2html/back-ends/extensions/signed/disable_web_security-0.0.3-an+fx.xpi


+ 0 - 0
cli/back-ends/extensions/signed/mozilla_archive_format_with_mht_and_faithful_save-5.2.1-fx+sm.xpi → maff2html/back-ends/extensions/signed/mozilla_archive_format_with_mht_and_faithful_save-5.2.1-fx+sm.xpi


BIN
maff2html/back-ends/extensions/signed/network_idle-0.0.2-an+fx.xpi


+ 149 - 0
maff2html/back-ends/webdriver-gecko.js

@@ -0,0 +1,149 @@
+/*
+ * Copyright 2010-2019 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile.
+ *
+ *   The code in this file is free software: you can redistribute it and/or 
+ *   modify it under the terms of the GNU Affero General Public License 
+ *   (GNU AGPL) as published by the Free Software Foundation, either version 3
+ *   of the License, or (at your option) any later version.
+ * 
+ *   The code in this file is distributed in the hope that it will be useful, 
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero 
+ *   General Public License for more details.
+ *
+ *   As additional permission under GNU AGPL version 3 section 7, you may 
+ *   distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU 
+ *   AGPL normally required by section 4, provided you include this license 
+ *   notice and a URL through which recipients can access the Corresponding 
+ *   Source.
+ */
+
+/* global require, exports, process */
+
+const fs = require("fs");
+
+const firefox = require("selenium-webdriver/firefox");
+const { Builder } = require("selenium-webdriver");
+
+const SCRIPTS = [
+	"../../lib/hooks/hooks-frame.js",
+	"../../lib/frame-tree/frame-tree.js",
+	"../../lib/lazy/content/content-lazy-loader.js",
+	"../../lib/single-file/util/doc-util.js",
+	"../../lib/single-file/util/doc-helper.js",
+	"../../lib/single-file/util/timeout.js",
+	"../../lib/single-file/vendor/css-tree.js",
+	"../../lib/single-file/vendor/html-srcset-parser.js",
+	"../../lib/single-file/vendor/css-minifier.js",
+	"../../lib/single-file/vendor/css-font-property-parser.js",
+	"../../lib/single-file/vendor/css-media-query-parser.js",
+	"../../lib/single-file/modules/html-minifier.js",
+	"../../lib/single-file/modules/css-fonts-minifier.js",
+	"../../lib/single-file/modules/css-fonts-alt-minifier.js",
+	"../../lib/single-file/modules/css-matched-rules.js",
+	"../../lib/single-file/modules/css-medias-alt-minifier.js",
+	"../../lib/single-file/modules/css-rules-minifier.js",
+	"../../lib/single-file/modules/html-images-alt-minifier.js",
+	"../../lib/single-file/modules/html-serializer.js",
+	"../../lib/single-file/single-file-core.js",
+	"../../lib/single-file/single-file-browser.js"
+];
+
+exports.getPageData = async options => {
+	const RESOLVED_CONTENTS = {
+		"lib/lazy/web/web-lazy-loader-before.js": fs.readFileSync(require.resolve("../../lib/lazy/web/web-lazy-loader-before.js")).toString(),
+		"lib/lazy/web/web-lazy-loader-after.js": fs.readFileSync(require.resolve("../../lib/lazy/web/web-lazy-loader-after.js")).toString()
+	};
+	let driver;
+	try {
+		const builder = new Builder().withCapabilities({ "pageLoadStrategy": "eager" });
+		const firefoxOptions = new firefox.Options();
+		options.enableMaff = true;
+		if (options.browserHeadless === undefined || options.browserHeadless) {
+			firefoxOptions.headless();
+		}
+		if (options.browserExecutablePath) {
+			firefoxOptions.setBinary(options.browserExecutablePath);
+		}
+		if (options.webDriverExecutablePath) {
+			process.env["webdriver.gecko.driver"] = options.webDriverExecutablePath;
+		}
+		const profile = new firefox.Profile();
+		if (options.browserDisableWebSecurity === undefined || options.browserDisableWebSecurity) {
+			profile.addExtension(require.resolve("./extensions/signed/disable_web_security-0.0.3-an+fx.xpi"));
+		}
+		if (options.browserBypassCSP === undefined || options.browserBypassCSP) {
+			profile.addExtension(require.resolve("./extensions/signed/bypass_csp-0.0.3-an+fx.xpi"));
+		}
+		if (options.browserWaitUntil === undefined || options.browserWaitUntil == "networkidle0" || options.browserWaitUntil == "networkidle2") {
+			profile.addExtension(require.resolve("./extensions/signed/network_idle-0.0.2-an+fx.xpi"));
+		}
+		if (options.userAgent) {
+			profile.setPreference("general.useragent.override", options.userAgent);
+		}
+		profile.addExtension(require.resolve("./extensions/signed/mozilla_archive_format_with_mht_and_faithful_save-5.2.1-fx+sm.xpi"));
+		firefoxOptions.setProfile(profile);
+		builder.setFirefoxOptions(firefoxOptions);
+		driver = await builder.forBrowser("firefox").build();
+		driver.manage().setTimeouts({ script: null, pageLoad: null, implicit: null });
+		if (options.browserWidth && options.browserHeight) {
+			const window = driver.manage().window();
+			if (window.setRect) {
+				window.setRect(options.browserHeight, options.browserWidth);
+			} else if (window.setSize) {
+				window.setSize(options.browserWidth, options.browserHeight);
+			}
+		}
+		let scripts = SCRIPTS.map(scriptPath => fs.readFileSync(require.resolve(scriptPath)).toString().replace(/\n(this)\.([^ ]+) = (this)\.([^ ]+) \|\|/g, "\nwindow.$2 = window.$4 ||")).join("\n");
+		scripts += "\nlazyLoader.getScriptContent = " + (function (path) { return (RESOLVED_CONTENTS)[path]; }).toString().replace("RESOLVED_CONTENTS", JSON.stringify(RESOLVED_CONTENTS)) + ";";
+		await driver.get(options.url);
+		if (options.browserWaitUntil === undefined || options.browserWaitUntil == "networkidle0") {
+			await driver.executeAsyncScript(scripts + "\naddEventListener(\"single-file-network-idle-0\", () => arguments[0](), true)");
+		} else if (options.browserWaitUntil == "networkidle2") {
+			await driver.executeAsyncScript(scripts + "\naddEventListener(\"single-file-network-idle-2\", () => arguments[0](), true)");
+		} else if (options.browserWaitUntil == "load") {
+			await driver.executeAsyncScript(scripts + "\nif (document.readyState == \"loading\" || document.readyState == \"interactive\") { document.addEventListener(\"load\", () => arguments[0]()) } else { arguments[0](); }");
+		} else {
+			await driver.executeScript(scripts);
+		}
+		const result = await driver.executeAsyncScript(getPageDataScript(), options);
+		if (result.error) {
+			throw result.error;
+		} else {
+			return result.pageData;
+		}
+	} finally {
+		if (driver) {
+			driver.quit();
+		}
+	}
+};
+
+function getPageDataScript() {
+	return `
+	const [options, callback] = arguments;
+	getPageData()
+		.then(pageData => callback({ pageData }))
+		.catch(error => callback({ error: error.toString() }));
+
+	async function getPageData() {
+		options.insertSingleFileComment = true;
+		const preInitializationPromises = [];
+		if (!options.saveRawPage) {
+			if (options.loadDeferredImages) {
+				preInitializationPromises.push(lazyLoader.process(options));
+			}
+		}
+		[options.framesData] = await Promise.all(preInitializationPromises);
+		options.doc = document;
+		options.win = window;
+		const SingleFile = SingleFileBrowser.getClass();
+		const singleFile = new SingleFile(options);
+		await singleFile.run();
+		return await singleFile.getPageData();
+	}
+	`;
+}

+ 16 - 0
maff2html/package.json

@@ -0,0 +1,16 @@
+{
+	"name": "single-file",
+	"version": "0.0.1",
+	"description": "SingleFile",
+	"author": "Gildas Lormeau",
+	"license": "AGPL-3.0-or-later",
+	"dependencies": {
+		"iconv-lite": "*",
+		"jsdom": "*",
+		"puppeteer-core": "*",
+		"request-promise-native": "*",
+		"selenium-webdriver": "3.*",
+		"strong-data-uri": "*",
+		"yargs": "*"
+	}
+}

+ 126 - 0
maff2html/single-file.js

@@ -0,0 +1,126 @@
+#!/usr/bin/env node
+
+/*
+ * Copyright 2010-2019 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ *
+ * This file is part of SingleFile.
+ *
+ *   The code in this file is free software: you can redistribute it and/or
+ *   modify it under the terms of the GNU Affero General Public License
+ *   (GNU AGPL) as published by the Free Software Foundation, either version 3
+ *   of the License, or (at your option) any later version.
+ *
+ *   The code in this file is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ *   General Public License for more details.
+ *
+ *   As additional permission under GNU AGPL version 3 section 7, you may
+ *   distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
+ *   AGPL normally required by section 4, provided you include this license
+ *   notice and a URL through which recipients can access the Corresponding
+ *   Source.
+ */
+
+/* global require */
+
+const args = require("yargs")
+	.wrap(null)
+	.command("$0 <url> [output]", "Save a page into a single HTML file.", yargs => {
+		yargs.positional("url", { description: "URL of the page to save", type: "string" });
+		yargs.positional("output", { description: "Output filename", type: "string" });
+	})
+	.default({
+		"browser-headless": true,
+		"browser-executable-path": "",
+		"browser-width": 1280,
+		"browser-height": 720,
+		"browser-wait-until": "networkidle0",
+		"compress-CSS": true,
+		"compress-HTML": true,
+		"filename-template": "",
+		"group-duplicate-images": true,
+		"load-deferred-images": true,
+		"load-deferred-images-max-idle-time": 1500,
+		"max-resource-size-enabled": false,
+		"max-resource-size": 10,
+		"remove-hidden-elements": true,
+		"remove-unused-styles": true,
+		"remove-unused-fonts": true,
+		"remove-frames": false,
+		"remove-imports": true,
+		"remove-scripts": true,
+		"remove-audio-src": true,
+		"remove-video-src": true,
+		"remove-alternative-fonts": true,
+		"remove-alternative-medias": true,
+		"remove-alternative-images": true,
+		"save-raw-page": false,
+		"web-driver-executable-path": ""
+	})
+	.options("browser-headless", { description: "Run the browser in headless mode" })
+	.boolean("browser-headless")
+	.options("browser-executable-path", { description: "Path to chrome/chromium executable" })
+	.string("browser-executable-path")
+	.options("browser-width", { description: "Width of the browser viewport in pixels" })
+	.number("browser-width")
+	.options("browser-height", { description: "Height of the browser viewport in pixels" })
+	.number("browser-height")
+	.options("browser-wait-until", { description: "When to consider the page is loaded" })
+	.choices("browser-wait-until", ["networkidle0", "networkidle2", "load", "domcontentloaded"])
+	.options("compress-CSS", { description: "Compress CSS stylesheets" })
+	.boolean("compress-CSS")
+	.options("compress-HTML", { description: "Compress HTML content" })
+	.boolean("compress-HTML")
+	.options("filename-template", { description: "Template used to generate the output filename (see help page of the extension for more info)" })
+	.string("filename-template")
+	.options("group-duplicate-images", { description: "Group duplicate images into CSS custom properties" })
+	.boolean("compress-HTML")
+	.options("load-deferred-images", { description: "Load deferred (a.k.a. lazy-loaded) images" })
+	.boolean("load-deferred-images")
+	.options("load-deferred-images-max-idle-time", { description: "Maximum delay of time to wait for deferred images" })
+	.number("load-deferred-images")
+	.options("max-resource-size-enabled", { description: "Enable removal of embedded resources exceeding a given size" })
+	.boolean("max-resource-size-enabled")
+	.options("max-resource-size", { description: "Maximum size of embedded resources (i.e. images, stylesheets, scripts and iframes)" })
+	.number("max-resource-size")
+	.options("remove-hidden-elements", { description: "Remove HTML elements which are not displayed" })
+	.number("remove-hidden-elements")
+	.options("remove-unused-styles", { description: "Remove unused CSS rules and unneeded declarations" })
+	.number("remove-unused-styles")
+	.options("remove-unused-fonts", { description: "Remove unused CSS font rules" })
+	.number("remove-unused-fonts")
+	.options("remove-frames", { description: "Remove frames" })
+	.number("remove-frames")
+	.options("remove-imports", { description: "Remove HTML imports" })
+	.number("remove-imports")
+	.options("remove-scripts", { description: "Remove JavaScript scripts" })
+	.number("remove-scripts")
+	.options("remove-audio-src", { description: "Remove source of audio elements" })
+	.number("remove-audio-src")
+	.options("remove-video-src", { description: "Remove source of video elements" })
+	.number("remove-video-src")
+	.options("remove-alternative-fonts", { description: "Remove alternative fonts to the ones displayed" })
+	.number("remove-alternative-fonts")
+	.options("remove-alternative-medias", { description: "Remove alternative CSS stylesheets" })
+	.number("remove-alternative-medias")
+	.options("remove-alternative-images", { description: "Remove images for alternative sizes of screen" })
+	.number("remove-alternative-images")
+	.options("save-raw-page", { description: "Save the original page without interpreting it into the browser" })
+	.number("save-raw-page")
+	.options("web-driver-executable-path", { description: "Path to Selenium WebDriver executable" })
+	.string("web-driver-executable-path")
+	.argv;
+
+require("./back-ends/webdriver-gecko.js").getPageData(args).then(pageData => {
+	if (args.output) {
+		require("fs").writeFileSync(args.output, pageData.content);
+	} else {
+		if (args.filenameTemplate) {
+			require("fs").writeFileSync(pageData.filename, pageData.content);
+		} else {
+			console.log(pageData.content); // eslint-disable-line no-console			
+		}
+	}
+});

Some files were not shown because too many files changed in this diff