From 7ef9967c8a17215575be6dca47fbddcd614bafbe Mon Sep 17 00:00:00 2001
From: hackademix <giorgio@maone.net>
Date: Oct 02 2018 21:00:20 +0000
Subject: [PATCH 1/6] Lenient WebLabels matching (by URL, by id, by name).


---

diff --git a/bg/ExternalLicenses.js b/bg/ExternalLicenses.js
index 27d9223..058b651 100644
--- a/bg/ExternalLicenses.js
+++ b/bg/ExternalLicenses.js
@@ -26,10 +26,27 @@
 "use strict";
 
 let licensesByLabel = new Map();
+let licensesByUrl = new Map();
 {
   let {licenses} = require("../license_definitions");
-  for (let l of Object.values(licenses).filter(l => l.identifier)) {
-    licensesByLabel.set(l.identifier, l);
+  for (let [id, l] of Object.entries(licenses)) {
+    let {identifier, canonicalUrl, licenseName} = l;
+    if (identifier) {
+      licensesByLabel.set(identifier, l);
+    } else {
+      l.identifier = id;
+    }
+    if (id !== identifier) {
+      licensesByLabel.set(id, l);
+    }
+    if (licenseName) {
+      licensesByLabel.set(licenseName, l);
+    }
+    if (Array.isArray(canonicalUrl)) {
+      for (let url of canonicalUrl) {
+        licensesByUrl.set(url, l);
+      }
+    }
   }
 }
 
@@ -55,23 +72,26 @@ var ExternalLicenses = {
       return null;
     }
     scriptInfo.licenses = new Set();
-    scriptInfo.allFree = true;
     scriptInfo.toString = function() {
       let licenseIds = [...this.licenses].map(l => l.identifier).sort().join(", ");
       return licenseIds
-         ? (this.allFree ? `Free license${this.licenses.length > 1 ? "s" : ""} (${licenseIds})`
-                         : `Mixed free (${licenseIds}) and unknown licenses`)
+         ? `Free license${this.licenses.size > 1 ? "s" : ""} (${licenseIds})`
          : "Unknown license(s)";
     }
-
-    for (let {label} of scriptInfo.licenseLinks) {
-      if (licensesByLabel.has(label)) {
-        scriptInfo.licenses.add(licensesByLabel.get(label));
-      } else {
-        scriptInfo.allFree = false;
-        break;
+    let match = (map, key) => {
+      if (map.has(key)) {
+        scriptInfo.licenses.add(map.get(key));
+        return true;
       }
+      return false;
+    };
+
+    for (let {label, url} of scriptInfo.licenseLinks) {
+      match(licensesByLabel, label = label.trim()) ||
+        match(licensesByUrl, url) ||
+        match(licensesByLabel, label.replace(/^GNU-|-(?:or-later|only)$/i, ''));
     }
+    scriptInfo.free = scriptInfo.licenses.size > 0;
     return scriptInfo;
   },
 
diff --git a/main_background.js b/main_background.js
index 4cfca7a..cdc987e 100644
--- a/main_background.js
+++ b/main_background.js
@@ -859,6 +859,7 @@ var ResponseHandler = {
 		let {request} = response;
 		let {url, type, tabId, frameId, documentUrl} = request;
 
+		let fullUrl = url;
 		url = ListStore.urlItem(url);
 		let site = ListStore.siteItem(url);
 
@@ -888,11 +889,11 @@ var ResponseHandler = {
 						"whitelisted": [url, whitelistedSite ? `User whitelisted ${site}` : "Whitelisted by user"]});
 					return ResponseProcessor.ACCEPT;
 				} else {
-					let scriptInfo = await ExternalLicenses.check({url, tabId, frameId, documentUrl});
+					let scriptInfo = await ExternalLicenses.check({url: fullUrl, tabId, frameId, documentUrl});
 					if (scriptInfo) {
 						let verdict, ret;
 						let msg = scriptInfo.toString();
-						if (scriptInfo.allFree) {
+						if (scriptInfo.free) {
 							verdict = "accepted";
 							ret = ResponseProcessor.ACCEPT;
 						} else {

From 37774e035b5ebf53e2fde92b11e4f3419b53909c Mon Sep 17 00:00:00 2001
From: hackademix <giorgio@maone.net>
Date: Oct 02 2018 21:00:20 +0000
Subject: [PATCH 2/6] WebLabels matching by id made case-insensitive.


---

diff --git a/bg/ExternalLicenses.js b/bg/ExternalLicenses.js
index 058b651..33bcbf6 100644
--- a/bg/ExternalLicenses.js
+++ b/bg/ExternalLicenses.js
@@ -29,18 +29,19 @@ let licensesByLabel = new Map();
 let licensesByUrl = new Map();
 {
   let {licenses} = require("../license_definitions");
+  let mapByLabel = (label, license) => licensesByLabel.set(label.toUpperCase(), license);
   for (let [id, l] of Object.entries(licenses)) {
     let {identifier, canonicalUrl, licenseName} = l;
     if (identifier) {
-      licensesByLabel.set(identifier, l);
+      mapByLabel(identifier, l);
     } else {
       l.identifier = id;
     }
     if (id !== identifier) {
-      licensesByLabel.set(id, l);
+      mapByLabel(id, l);
     }
     if (licenseName) {
-      licensesByLabel.set(licenseName, l);
+      mapByLabel(licenseName, l);
     }
     if (Array.isArray(canonicalUrl)) {
       for (let url of canonicalUrl) {
@@ -87,9 +88,9 @@ var ExternalLicenses = {
     };
 
     for (let {label, url} of scriptInfo.licenseLinks) {
-      match(licensesByLabel, label = label.trim()) ||
+      match(licensesByLabel, label = label.trim().toUpperCase()) ||
         match(licensesByUrl, url) ||
-        match(licensesByLabel, label.replace(/^GNU-|-(?:or-later|only)$/i, ''));
+        match(licensesByLabel, label.replace(/^GNU-|-(?:OR-LATER|ONLY)$/, ''));
     }
     scriptInfo.free = scriptInfo.licenses.size > 0;
     return scriptInfo;

From 43502637288f9f60019be2759e5049d0fe9377fe Mon Sep 17 00:00:00 2001
From: hackademix <giorgio@maone.net>
Date: Oct 02 2018 21:00:21 +0000
Subject: [PATCH 3/6] Jasmine-based automated tests suite.


---

diff --git a/.gitignore b/.gitignore
index 9a139c8..3314ced 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ SDK LibreJS source/
 
 # dependencies
 node_modules/
+test/lib/
 
 # artifacts
 librejs.xpi
diff --git a/README b/README
index a380900..f5cf254 100644
--- a/README
+++ b/README
@@ -15,6 +15,11 @@ To build the extension run:
 To build the extension plus create a .xpi package run:
   $ ./build.sh
 
+To build the extension including the automated test suite (see TEST below) run:
+  $ ./build.sh -t
+  or
+  $ ./build.sh --test
+
 Note: this build.sh script relies on no new source files being created.
 
 
@@ -24,6 +29,12 @@ To debug this add-on on IceCat and other Firefox derivatives, browse to the spec
 
 LibreJS should work with other WebExtensions-compliant browsers; but currently, none of them meet the freedom standards of GNU, so no help will be provided for their usage.
 
+TEST:
+
+An automated test suite runs automatically in its own tab whenever the extension
+is loaded as a "temporary add-on" from about:debugging.
+Otherwise (if included in the xpi, see BUILD above) it can be launched from the
+UI by clicking the [Automated self test...] button.
 
 CONTACT:
 
diff --git a/build.sh b/build.sh
index 1e07fcb..0cd41d7 100755
--- a/build.sh
+++ b/build.sh
@@ -1,6 +1,16 @@
 PATH=$PATH:./node_modules/.bin
 which browserify > /dev/null || (echo "can not find browserify" && false) || exit
 
+# prepare / update testing environment if needed
+JASMINE_VER=3.2.1
+JASMINE_LIB="test/lib/jasmine-$JASMINE_VER"
+if ! [ -d "$JASMINE_LIB" ]; then
+  mkdir -p test/lib
+  rm -rf test/lib/jasmine-*
+  JASMINE_URL="https://github.com/jasmine/jasmine/releases/download/v$JASMINE_VER/jasmine-standalone-$JASMINE_VER.zip"
+  curl -L -o "$JASMINE_LIB.zip" "$JASMINE_URL" && unzip -d test/ $JASMINE_LIB.zip lib/**
+  rm -f "$JASMINE_LIB.zip"
+fi
 
 # Build the main file
 browserify main_background.js -o bundle.js
@@ -9,7 +19,10 @@ browserify main_background.js -o bundle.js
 mkdir ./build_temp
 
 # Move source files to temp directory
-cp -r icons ./build_temp
+if [ "$1" == "-t" -o "$1" == "--test" ]; then
+  cp -r ./test ./build_temp
+fi
+cp -r ./icons ./build_temp
 cp -r ./html ./build_temp
 cp -r ./content ./build_temp
 cp -r ./common ./build_temp
diff --git a/common/Test.js b/common/Test.js
new file mode 100644
index 0000000..e272d1e
--- /dev/null
+++ b/common/Test.js
@@ -0,0 +1,55 @@
+/**
+* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+*
+* Copyright (C) 2018 Giorgio Maone <giorgio@maone.net>
+*
+* This file is part of GNU LibreJS.
+*
+* GNU LibreJS is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* GNU LibreJS 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with GNU LibreJS.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+"use strict";
+var Test = (() => {
+  const RUNNER_URL = browser.extension.getURL("/test/SpecRunner.html");
+  return {
+    /*
+      returns RUNNER_URL if it's a test-enabled build or an about:debugging
+      temporary extension session, null otherwise
+    */
+    async getURL() {
+      let url = RUNNER_URL;
+      try {
+        await fetch(url);
+      } catch (e) {
+        url = null;
+      }
+      this.getURL = () => url;
+      return url;
+    },
+
+    async getTab(activate = false) {
+      let url = await this.getURL();
+      let tab = url ? (await browser.tabs.query({url}))[0] ||
+                    (await browser.tabs.create({url}))
+        : null;
+      if (tab && activate) {
+        await browser.tabs.update(tab.id, {active: true});
+      }
+      return tab;
+    }
+  };
+})();
+if (typeof module === "object") {
+  module.exports = Test;
+}
diff --git a/html/common.css b/html/common.css
index cf2c5d1..14931fa 100644
--- a/html/common.css
+++ b/html/common.css
@@ -3,6 +3,7 @@ html {
   margin:0px;
   color:#000 !important;
   background:url('background-panel.png') !important;
+  font-family: sans-serif;
 }
 body {
   padding:0;
@@ -26,4 +27,15 @@ h1.libre {
     font-weight:bold;
     background:url('librejs-title.png') no-repeat top left;
     text-indent:-1000px;
+    position: relative;
+}
+h1.libre span {
+  font-family: sans-serif;
+  position: absolute;
+  bottom: 32px;
+  padding: 0 16px;
+  left: 230px;
+  display: block;
+  text-indent: 0;
+  vertical-align: bottom;
 }
diff --git a/html/display_panel/content/display-panel.html b/html/display_panel/content/display-panel.html
index df153b3..80f4cb9 100644
--- a/html/display_panel/content/display-panel.html
+++ b/html/display_panel/content/display-panel.html
@@ -53,6 +53,7 @@
           <button id="complain">Complain to site owner</button>
          	<button id="report-tab">Show this report in a new tab</button>
           <button id="open-options">Settings...</button>
+          <button id="autotest">Automated self test...</button>
         </div>
     </div>
     <div id="info">
@@ -100,5 +101,6 @@
         </div>
     </div>
 </body>
+<script src="/common/Test.js"></script>
 <script src="main_panel.js"></script>
 </html>
diff --git a/html/display_panel/content/main_panel.js b/html/display_panel/content/main_panel.js
index c55b167..2509545 100644
--- a/html/display_panel/content/main_panel.js
+++ b/html/display_panel/content/main_panel.js
@@ -38,6 +38,18 @@ myPort.postMessage({"update": true, tabId: parseInt(currentReport && currentRepo
 // Display the actual extension version Number
 document.querySelector("#version").textContent = browser.runtime.getManifest().version;
 
+// Enable autotest button if this is a test-enabled build / session
+(async () => {
+  if (await Test.getURL()) {
+    let button = document.querySelector("#autotest");
+    button.style.display = "block";
+    button.onclick = async () => {
+      await Test.getTab(true);
+      close();
+    }
+  }
+})();
+
 var liTemplate = document.querySelector("#li-template");
 liTemplate.remove();
 
diff --git a/html/display_panel/content/panel-styles.css b/html/display_panel/content/panel-styles.css
index 0d849e4..940a8ff 100644
--- a/html/display_panel/content/panel-styles.css
+++ b/html/display_panel/content/panel-styles.css
@@ -141,7 +141,8 @@ span.blocked {
   display: none;
 }
 
-.tab #must-reload, .tab #buttons, .empty #buttons {
+.tab #must-reload, .tab #buttons,
+.empty #complain, .empty #report-tab, #autotest {
   display: none;
 }
 
diff --git a/main_background.js b/main_background.js
index cdc987e..5c2e8df 100644
--- a/main_background.js
+++ b/main_background.js
@@ -1175,6 +1175,21 @@ async function init_addon() {
 	ResponseProcessor.install(ResponseHandler);
 
 	legacy_license_lib.init();
+
+
+	let Test = require("./common/Test");
+	if (Test.getURL()) {
+		// export testable functions to the global scope
+		this.LibreJS = {
+			editHtml,
+			handle_script,
+			ExternalLicenses,
+		};
+		// create or focus the autotest tab if it's a debugging session
+		if ((await browser.management.getSelf()).installType === "development") {
+			Test.getTab(true);
+		}
+	}
 }
 
 
diff --git a/test/SpecRunner.html b/test/SpecRunner.html
new file mode 100644
index 0000000..42a372f
--- /dev/null
+++ b/test/SpecRunner.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<!--
+/**
+* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+*
+* Copyright (C) 2018 Giorgio Maone <giorgio@maone.net>
+*
+* This file is part of GNU LibreJS.
+*
+* GNU LibreJS is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* GNU LibreJS 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with GNU LibreJS.  If not, see <http://www.gnu.org/licenses/>.
+*/
+-->
+<head>
+  <meta charset="utf-8">
+  <title>LibreJS Tests</title>
+
+  <link rel="shortcut icon" type="image/png" href="/icons/librejs.png">
+  <link rel="stylesheet" href="lib/jasmine-3.2.1/jasmine.css">
+  <link rel="stylesheet" href="/html/common.css">
+  <script src="lib/jasmine-3.2.1/jasmine.js"></script>
+  <script src="lib/jasmine-3.2.1/jasmine-html.js"></script>
+  <script src="lib/jasmine-3.2.1/boot.js"></script>
+
+  <script src="spec/LibreJSSpec.js"></script>
+
+</head>
+
+<body>
+  <h1 class="libre">LibreJS <span>Autotest</span></h1>
+</body>
+</html>
diff --git a/test/resources/app-trilicensed.js b/test/resources/app-trilicensed.js
new file mode 100644
index 0000000..7b2c2ea
--- /dev/null
+++ b/test/resources/app-trilicensed.js
@@ -0,0 +1 @@
+document.write(`<p>Executing ${document.currentScript.src}</p>`);
diff --git a/test/resources/index.html b/test/resources/index.html
new file mode 100644
index 0000000..c5f364c
--- /dev/null
+++ b/test/resources/index.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<!--
+/**
+* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+*
+* Copyright (C) 2018 Giorgio Maone <giorgio@maone.net>
+*
+* This file is part of GNU LibreJS.
+*
+* GNU LibreJS is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* GNU LibreJS 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with GNU LibreJS.  If not, see <http://www.gnu.org/licenses/>.
+*/
+-->
+<head>
+  <meta charset="utf-8">
+  <title>LibreJS test document</title>
+  <link rel="shortcut icon" type="image/png" href="/icons/librejs.png">
+</head>
+<body>
+<h1>LibreJS test document</h1>
+<a href="jslicense.html" rel="jslicense">JavaScript license information</a>
+<script src="jquery.js"></script>
+<script src="app-trilicensed.js"></script>
+<script src="proprietary.js"></script>
+<script src="tracker.js"></script>
+</body>
+</html>
diff --git a/test/resources/jquery.js b/test/resources/jquery.js
new file mode 100644
index 0000000..7b2c2ea
--- /dev/null
+++ b/test/resources/jquery.js
@@ -0,0 +1 @@
+document.write(`<p>Executing ${document.currentScript.src}</p>`);
diff --git a/test/resources/jslicense.html b/test/resources/jslicense.html
new file mode 100644
index 0000000..0cd0449
--- /dev/null
+++ b/test/resources/jslicense.html
@@ -0,0 +1,25 @@
+<table id="jslicense-labels1">
+<tr>
+<td><a href="jquery.js">Fake jQuery</a></td>
+<td><a href="http://www.jclark.com/xml/copying.txt">Expat</a>
+<td><a href="jquery-1.7.tar.gz">jquery-1.7.tar.gz</a></td>
+</tr>
+<tr>
+<td><a href="app-trilicensed.js">App specific script, tri-licensed (2 free and 1 proprietary license)</a></td>
+<td>
+<a href="magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt">GNU V3</a>
+<br>
+<a href="http://www.jclark.com/xml/copying.txt">Expat</a>
+<br>
+<a href="https://evil.com/someproprietarylicense">Proprietary, optional</a></td>
+</td>
+<td><a href="app-trilicensed.js">app-trilicensed.js</a></td>
+</tr>
+<tr>
+<td><a href="proprietary.js">App specific script, proprietary only</a></td>
+<td>
+<a href="https://evil.com/someproprietarylicense">Proprietary</a></td>
+</td>
+<td><a href="proprietary.js">proprietary.js</a></td>
+</tr>
+</table>
diff --git a/test/resources/proprietary.js b/test/resources/proprietary.js
new file mode 100644
index 0000000..7b2c2ea
--- /dev/null
+++ b/test/resources/proprietary.js
@@ -0,0 +1 @@
+document.write(`<p>Executing ${document.currentScript.src}</p>`);
diff --git a/test/resources/tracker.js b/test/resources/tracker.js
new file mode 100644
index 0000000..7b2c2ea
--- /dev/null
+++ b/test/resources/tracker.js
@@ -0,0 +1 @@
+document.write(`<p>Executing ${document.currentScript.src}</p>`);
diff --git a/test/spec/LibreJSSpec.js b/test/spec/LibreJSSpec.js
new file mode 100644
index 0000000..8638d67
--- /dev/null
+++ b/test/spec/LibreJSSpec.js
@@ -0,0 +1,212 @@
+/*
+* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+*
+* Copyright (C) 2018 Giorgio Maone <giorgio@maone.net>
+*
+* This file is part of GNU LibreJS.
+*
+* GNU LibreJS is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* GNU LibreJS 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with GNU LibreJS.  If not, see <http://www.gnu.org/licenses/>.
+*/
+"use strict";
+
+describe("LibreJS' components", () => {
+  const LENIENT_TAG_MSG =
+    `actual requirement? at this moment (20181001)
+      formal validity only gets checked, therefore this test would fail.`;
+
+  let LibreJS = browser.extension.getBackgroundPage().LibreJS;
+  let license = {
+    id: 'GPL-3.0',
+    url: 'http://www.gnu.org/licenses/gpl-3.0.html',
+    magnet: 'magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt',
+  };
+  let unknownLicense = {
+    id: 'Acme-proprietary-1.5',
+    url: 'http://www.acme.com/license-1.5.html',
+    magnet: 'magnet:?xt=urn:btih:2f739d935676111cfff4b4693e3816e664797050&dn=acme-1.5.txt'
+  };
+
+  let trivial = "1+1";
+  let nontrivial = `function nt() { document.documentElement.innerHTML=""; nt(); }`;
+  let licensed = `// @license ${license.id} ${license.magnet}\n${nontrivial}\n// @license-end`;
+  let unknownLicensed = `// @license ${unknownLicense.id} ${unknownLicense.magnet}\n${nontrivial}\n// @license-end`;
+  let malformedLicensed = `// @license\n${nontrivial}`;
+
+  let tab;
+
+  beforeAll(async () => {
+    let url = browser.extension.getURL("/test/resources/index.html");
+    tab = (await browser.tabs.query({url}))[0] || (await browser.tabs.create({url}));
+  });
+
+  describe("The external script source processor", () => {
+    let url = "https://www.gnu.org/mock-script.js";
+
+    let processScript = async (source, whitelisted = false) =>
+      await LibreJS.handle_script({
+        text: source,
+        request: {url, tabId: tab.id, documentUrl: tab.url, frameId: 0},
+      }, whitelisted);
+
+    it("should accept whitelisted scripts", async () => {
+      expect(await processScript(nontrivial, true) || nontrivial).toContain(nontrivial);
+    });
+
+    it("should block trivial scripts too", async () => {
+      let processed = await processScript(trivial);
+      expect(processed || trivial).not.toContain(trivial);
+    });
+
+    it("should block non-trivial scripts", async () => {
+      let processed = await processScript(nontrivial);
+      expect(processed || nontrivial).not.toContain(nontrivial);
+    });
+
+    it("should accept scripts with known free license tags", async () => {
+      let processed = await processScript(licensed);
+      expect(processed || licensed).toContain(nontrivial);
+    });
+
+    xit("should block scripts with unknown license tags", async () => {
+      let processed = await processScript(unknownLicensed);
+      expect(processed).not.toContain(nontrivial);
+    }).pend(LENIENT_TAG_MSG);
+
+    it("should block scripts with malformed license tags", async () => {
+      let processed = await processScript(malformedLicensed);
+      expect(processed).not.toContain(nontrivial);
+    });
+  });
+
+  describe("The HTML processor", () => {
+    let processHtml =
+      async (html, whitelisted = false) =>
+        LibreJS.editHtml(html, tab.url, tab.id, 0, whitelisted);
+
+    let addScript = (html, script, before = "</head>") =>
+      html.replace(before, `<script>${script}</script>${before}`);
+
+    function extractScripts(html, def = "") {
+      let matches = html && html.match(/<script>[^]*?<\/script>/g);
+      return matches && matches.join("") || def;
+    }
+
+    let html, nontrivialInHtml;
+    beforeAll(async () => {
+      html = (await browser.tabs.executeScript(tab.id, {
+        runAt: "document_start",
+        code: "document.documentElement.outerHTML"
+      }))[0];
+      nontrivialInHtml = addScript(html, nontrivial);
+    });
+
+    it("should not modify scriptless documents", async () => {
+      expect(await processHtml(html)).toBeNull();
+    });
+
+    it("should not modify whitelisted documents", async () => {
+      expect(await processHtml(nontrivialInHtml, true)).toBeNull();
+    });
+
+    it("should accept trivial scripts", async () => {
+      let trivialInHtml = addScript(html, trivial);
+      let processed = await processHtml(trivialInHtml);
+      expect(extractScripts(processed, trivial)).toContain(trivial);
+    });
+
+    it("should block non-trivial scripts", async () => {
+      let processed = await processHtml(nontrivialInHtml);
+      expect(extractScripts(processed, nontrivial)).not.toContain(nontrivial);
+    });
+
+    it("should accept scripts with known free license tags", async () => {
+      let licensedInHtml = addScript(html, licensed);
+      let processed = await processHtml(licensedInHtml);
+      expect(extractScripts(processed, licensed)).toContain(nontrivial);
+    });
+
+    xit("should block scripts with unknown license tags", async () => {
+      let unknownInHtml = addScript(html, unknownLicensed);
+      let processed = await processHtml(unknownInHtml);
+      expect(extractScripts(processed, nontrivial)).not.toContain(nontrivial);
+    }).pend(LENIENT_TAG_MSG);
+
+    it("should block scripts with malformed license tags", async () => {
+      let malformedInHtml = addScript(html, malformedLicensed);
+      let processed = await processHtml(malformedInHtml);
+      expect(extractScripts(processed, nontrivial)).not.toContain(nontrivial);
+    });
+
+    it("should accept scripts on globally licensed pages", async () => {
+      let globalLicense = `/* @licstart The following is the entire license notice
+        for the JavaScript code in this page.
+        -- Some free license --
+        @licend The above is the entire license notice for the JavaScript code in this page. */`;
+
+      let licensed = addScript(nontrivialInHtml, globalLicense, "<script>");
+      let processed = await processHtml(html);
+      expect(extractScripts(processed, licensed)).toContain(nontrivial);
+    });
+
+    it("should discriminate trivial, non-trivial and licensed mixed on the same page", async () => {
+      let mixedPage = addScript(addScript(nontrivialInHtml, trivial), licensed);
+      let processed = await processHtml(mixedPage);
+      expect(processed).not.toBeNull();
+      let scripts = extractScripts(processed, nontrivial);
+      expect(scripts).toContain(trivial);
+      expect(scripts).toContain(licensed);
+      expect(scripts.replace(licensed, "")).not.toContain(nontrivial);
+    });
+  });
+
+  describe("The external (Web Labels) license checker", () => {
+    let {ExternalLicenses} = LibreJS;
+    let check;
+
+    beforeAll(async () => {
+      let documentUrl = tab.url;
+      let args = {tabId: tab.id, frameId: 0, documentUrl};
+      let resolve = url => new URL(url, documentUrl).href;
+      check = async url => await ExternalLicenses.check(Object.assign({url: resolve(url)}, args));
+      await browser.tabs.executeScript(tab.id, {
+        file: "/content/externalLicenseChecker.js"
+      });
+    });
+
+    it("should recognize free licenses", async () => {
+      let scriptInfo = await check("jquery.js");
+      console.debug(scriptInfo);
+      expect(scriptInfo.free).toBeTruthy();
+    });
+    it("should accept scripts if any of multiple licenses is free", async () => {
+      let scriptInfo = await check("app-trilicensed.js");
+      console.debug(scriptInfo);
+      expect(scriptInfo.free).toBeTruthy();
+    });
+    it("should block scripts declaring only proprietary license(s)", async () => {
+      let scriptInfo = await check("proprietary.js");
+      console.debug(scriptInfo);
+      expect(scriptInfo.free).toBeFalsy();
+    });
+    it("should block scripts not declaring any license", async () => {
+      let scriptInfo = await check("tracker.js");
+      console.debug(scriptInfo);
+      expect(scriptInfo).toBeNull();
+    });
+  });
+  afterAll(async () => {
+    // await browser.tabs.remove(tab.id);
+    browser.tabs.update((await browser.tabs.getCurrent()).id, {active: true});
+  });
+});

From 57a58a82609cc5130942676514b6e99b6f6f317f Mon Sep 17 00:00:00 2001
From: hackademix <giorgio@maone.net>
Date: Oct 02 2018 21:00:21 +0000
Subject: [PATCH 4/6] Restored testing page tab removal after completion.


---

diff --git a/test/spec/LibreJSSpec.js b/test/spec/LibreJSSpec.js
index 8638d67..b2ca40e 100644
--- a/test/spec/LibreJSSpec.js
+++ b/test/spec/LibreJSSpec.js
@@ -206,7 +206,7 @@ describe("LibreJS' components", () => {
     });
   });
   afterAll(async () => {
-    // await browser.tabs.remove(tab.id);
+    await browser.tabs.remove(tab.id);
     browser.tabs.update((await browser.tabs.getCurrent()).id, {active: true});
   });
 });

From c82d0a82f4246667e1ac1238be4121fcaffdde6a Mon Sep 17 00:00:00 2001
From: hackademix <giorgio@maone.net>
Date: Oct 02 2018 21:00:22 +0000
Subject: [PATCH 5/6] Work around for documentUrl = tab.url = about:blank if tests are launched from XPI


---

diff --git a/test/spec/LibreJSSpec.js b/test/spec/LibreJSSpec.js
index b2ca40e..eba69b5 100644
--- a/test/spec/LibreJSSpec.js
+++ b/test/spec/LibreJSSpec.js
@@ -43,11 +43,12 @@ describe("LibreJS' components", () => {
   let unknownLicensed = `// @license ${unknownLicense.id} ${unknownLicense.magnet}\n${nontrivial}\n// @license-end`;
   let malformedLicensed = `// @license\n${nontrivial}`;
 
-  let tab;
+  let tab, documentUrl;
 
   beforeAll(async () => {
     let url = browser.extension.getURL("/test/resources/index.html");
     tab = (await browser.tabs.query({url}))[0] || (await browser.tabs.create({url}));
+    documentUrl = url;
   });
 
   describe("The external script source processor", () => {
@@ -56,7 +57,7 @@ describe("LibreJS' components", () => {
     let processScript = async (source, whitelisted = false) =>
       await LibreJS.handle_script({
         text: source,
-        request: {url, tabId: tab.id, documentUrl: tab.url, frameId: 0},
+        request: {url, tabId: tab.id, documentUrl, frameId: 0},
       }, whitelisted);
 
     it("should accept whitelisted scripts", async () => {
@@ -175,7 +176,6 @@ describe("LibreJS' components", () => {
     let check;
 
     beforeAll(async () => {
-      let documentUrl = tab.url;
       let args = {tabId: tab.id, frameId: 0, documentUrl};
       let resolve = url => new URL(url, documentUrl).href;
       check = async url => await ExternalLicenses.check(Object.assign({url: resolve(url)}, args));

From 12bfe0f9fdad5bfea680d897c81319684ee82180 Mon Sep 17 00:00:00 2001
From: hackademix <giorgio@maone.net>
Date: Oct 02 2018 21:00:22 +0000
Subject: [PATCH 6/6] Adjusting for the fixes to the malformed license parsing.


---

diff --git a/test/spec/LibreJSSpec.js b/test/spec/LibreJSSpec.js
index eba69b5..3d61973 100644
--- a/test/spec/LibreJSSpec.js
+++ b/test/spec/LibreJSSpec.js
@@ -21,10 +21,6 @@
 "use strict";
 
 describe("LibreJS' components", () => {
-  const LENIENT_TAG_MSG =
-    `actual requirement? at this moment (20181001)
-      formal validity only gets checked, therefore this test would fail.`;
-
   let LibreJS = browser.extension.getBackgroundPage().LibreJS;
   let license = {
     id: 'GPL-3.0',
@@ -39,8 +35,8 @@ describe("LibreJS' components", () => {
 
   let trivial = "1+1";
   let nontrivial = `function nt() { document.documentElement.innerHTML=""; nt(); }`;
-  let licensed = `// @license ${license.id} ${license.magnet}\n${nontrivial}\n// @license-end`;
-  let unknownLicensed = `// @license ${unknownLicense.id} ${unknownLicense.magnet}\n${nontrivial}\n// @license-end`;
+  let licensed = `// @license ${license.magnet} ${license.id}\n${nontrivial}\n// @license-end`;
+  let unknownLicensed = `// @license ${unknownLicense.magnet} ${unknownLicense.id}\n${nontrivial}\n// @license-end`;
   let malformedLicensed = `// @license\n${nontrivial}`;
 
   let tab, documentUrl;
@@ -79,10 +75,10 @@ describe("LibreJS' components", () => {
       expect(processed || licensed).toContain(nontrivial);
     });
 
-    xit("should block scripts with unknown license tags", async () => {
+    it("should block scripts with unknown license tags", async () => {
       let processed = await processScript(unknownLicensed);
       expect(processed).not.toContain(nontrivial);
-    }).pend(LENIENT_TAG_MSG);
+    });
 
     it("should block scripts with malformed license tags", async () => {
       let processed = await processScript(malformedLicensed);
@@ -137,11 +133,11 @@ describe("LibreJS' components", () => {
       expect(extractScripts(processed, licensed)).toContain(nontrivial);
     });
 
-    xit("should block scripts with unknown license tags", async () => {
+    it("should block scripts with unknown license tags", async () => {
       let unknownInHtml = addScript(html, unknownLicensed);
       let processed = await processHtml(unknownInHtml);
       expect(extractScripts(processed, nontrivial)).not.toContain(nontrivial);
-    }).pend(LENIENT_TAG_MSG);
+    });
 
     it("should block scripts with malformed license tags", async () => {
       let malformedInHtml = addScript(html, malformedLicensed);

