From af7333cdc8096b79bc717ee4f334a053ec74ca4d Mon Sep 17 00:00:00 2001 From: Carlos Quintana Date: Tue, 17 May 2022 12:31:47 +0200 Subject: [PATCH] Use new extension setup process --- scripts/build-zip.js | 4 +- src/background/index.js | 36 ++- src/background/onboarding.js | 23 +- src/content_script/input_tools.js | 449 ++++++++++++++++-------------- src/onboarding/Onboarding.vue | 151 ---------- src/onboarding/index.html | 13 - src/onboarding/index.js | 12 - src/onboarding/styles.scss | 67 ----- webpack.config.js | 2 - 9 files changed, 276 insertions(+), 481 deletions(-) delete mode 100644 src/onboarding/Onboarding.vue delete mode 100644 src/onboarding/index.html delete mode 100644 src/onboarding/index.js delete mode 100644 src/onboarding/styles.scss diff --git a/scripts/build-zip.js b/scripts/build-zip.js index 9f9a70c..28235ff 100644 --- a/scripts/build-zip.js +++ b/scripts/build-zip.js @@ -16,14 +16,14 @@ const extractExtensionData = () => { return { name: extPackageJson.name + (isBeta ? '-beta' : '-release'), version: extPackageJson.version + (isBeta ? ('.' + betaRev) : ''), - } + }; }; const makeDestZipDirIfNotExists = () => { if(!fs.existsSync(DEST_ZIP_DIR)) { fs.mkdirSync(DEST_ZIP_DIR); } -} +}; const buildZip = (src, dist, zipFilename) => { console.info(`Building ${zipFilename}...`); diff --git a/src/background/index.js b/src/background/index.js index 1cbf79a..25fa0c8 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,5 +1,5 @@ import browser from "webextension-polyfill"; -import APIService from "../popup/APIService"; +import APIService, { API_ROUTE } from "../popup/APIService"; import SLStorage from "../popup/SLStorage"; import Onboarding from "./onboarding"; import "./content-script"; @@ -9,6 +9,8 @@ import { handleOnClickContextMenu, generateAliasHandlerJS, } from "./context-menu"; +import axios from "axios"; +import Utils from "../popup/Utils"; global.isBackgroundJS = true; @@ -26,6 +28,36 @@ async function handleGetAppSettings() { }; } +async function handleExtensionSetup() { + const apiUrl = await SLStorage.get(SLStorage.SETTINGS.API_URL); + try { + const url = apiUrl + API_ROUTE.GET_API_KEY_FROM_COOKIE.path; + const res = await axios.post(url, { + device: Utils.getDeviceName(), + }); + + const apiKey = res.data.api_key; + if (apiKey) { + await SLStorage.set(SLStorage.SETTINGS.API_KEY, apiKey); + + const currentTab = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + + const url = `${apiUrl}/onboarding/final`; + await browser.tabs.update(currentTab[0].id, { + url, + }); + } else { + console.error("Received null API Key"); + } + } catch (e) { + // Probably the user is not logged in + console.error(e); + } +} + /** * Register onMessage listener */ @@ -34,6 +66,8 @@ browser.runtime.onMessage.addListener(async function (request, sender) { return await handleNewRandomAlias(request.currentUrl); } else if (request.tag === "GET_APP_SETTINGS") { return await handleGetAppSettings(); + } else if (request.tag === "EXTENSION_SETUP") { + return await handleExtensionSetup(); } }); diff --git a/src/background/onboarding.js b/src/background/onboarding.js index 3fcb566..c189bd5 100644 --- a/src/background/onboarding.js +++ b/src/background/onboarding.js @@ -1,32 +1,15 @@ import browser from "webextension-polyfill"; import SLStorage from "../popup/SLStorage"; -const delay = (ms) => new Promise((r) => setTimeout(r, ms)); - function initService() { + browser.runtime.onInstalled.addListener(async function ({ reason }) { if (reason === "install") { await SLStorage.clear(); + const apiUrl = await SLStorage.get(SLStorage.SETTINGS.API_URL); await browser.tabs.create({ - url: browser.runtime.getURL("/onboarding/index.html"), - }); - - // listen for post-setup screen - browser.cookies.onChanged.addListener(async function (info) { - const { removed, cookie } = info; - if (!removed && cookie.name === "setup_done") { - const currentTab = await browser.tabs.query({ - active: true, - currentWindow: true, - }); - - await delay(100); - - await browser.tabs.update(currentTab[0].id, { - url: browser.runtime.getURL("/onboarding/index.html#step3"), - }); - } + url: `${apiUrl}/onboarding`, }); } }); diff --git a/src/content_script/input_tools.js b/src/content_script/input_tools.js index 45a6047..63d31b2 100644 --- a/src/content_script/input_tools.js +++ b/src/content_script/input_tools.js @@ -1,214 +1,12 @@ -if (!window.hasSLButton) { - window.hasSLButton = true; - let SLSettings = {}; - - const InputTools = { - isLoading: false, - - // store tracked input elements - trackedElements: [], - - init(target) { - InputTools.queryEmailInputAndApply(target, (element) => { - if (!InputTools.isValidEmailInput(element)) { - return; - } - - // ignore if this elements has already been tracked - const i = InputTools.trackedElements.indexOf(element); - if (i === -1) { - InputTools.trackedElements.push(element); - InputTools.addSLButtonToInput(element); - } - }); - }, - - destroy(target) { - InputTools.queryEmailInputAndApply(target, (element) => { - // remove element from tracking list - const i = InputTools.trackedElements.indexOf(element); - if (i !== -1) { - InputTools.trackedElements.splice(i, 1); - } - }); - }, - - queryEmailInputAndApply(target, actionFunction) { - if (!target.querySelectorAll) return; - const elements = target.querySelectorAll( - "input[type='email'],input[name*='email'],input[id*='email']" - ); - for (const element of elements) { - actionFunction(element); - } - }, - - isValidEmailInput(element) { - const style = getComputedStyle(element); - return ( - // check if element is visible - style.visibility !== "hidden" && - style.display !== "none" && - style.opacity !== "0" && - style.pointerEvents === "auto" && - // check if element is not disabled - !element.disabled && - // for example, we must filter out a checkbox with name*=email - // check if element is text or email input - (element.type === "text" || element.type === "email") - ); - }, - - addSLButtonToInput(inputElem) { - // create wrapper for SL button - const btnWrapper = InputTools.newDiv("sl-button-wrapper"); - const inputSumHeight = inputElem.getBoundingClientRect().height + "px"; - btnWrapper.style.height = inputSumHeight; - btnWrapper.style.width = inputSumHeight; - document.body.appendChild(btnWrapper); - - // create the SL button - const slButton = InputTools.newDiv("sl-button"); - slButton.addEventListener("click", function () { - InputTools.handleOnClickSLButton(inputElem, slButton); - }); - slButton.style.height = inputSumHeight; - slButton.style.width = inputSumHeight; - btnWrapper.appendChild(slButton); - - InputTools.placeBtnToTheRightOfElement(inputElem, btnWrapper); - }, - - newDiv(...className) { - const div = document.createElement("div"); - div.classList.add(...className); - return div; - }, - - placeBtnToTheRightOfElement(inputElem, btnWrapper) { - let intervalId = 0; - - function updatePosition() { - // check is element is removed from trackedElements - const i = InputTools.trackedElements.indexOf(inputElem); - if (i === -1) { - btnWrapper.parentNode.removeChild(btnWrapper); - clearInterval(intervalId); - } - - // get dimension & position of input - const inputCoords = inputElem.getBoundingClientRect(); - const inputStyle = getComputedStyle(inputElem); - const elemWidth = InputTools.dimensionToInt(btnWrapper.style.width); - const pageXOffset = window.pageXOffset; - const pageYOffset = window.pageYOffset; - const buttonXOffset = - SLSettings.SLButtonPosition === "right-inside" - ? -elemWidth * 1.02 - : elemWidth * 0.02; - - // calculate elem position - const left = - InputTools.sumPixel([ - inputCoords.left, - pageXOffset, - inputElem.offsetWidth, - buttonXOffset, - -inputStyle.paddingRight, - ]) + "px"; - - const top = InputTools.sumPixel([inputCoords.top, pageYOffset]) + "px"; - - if (btnWrapper.style.left !== left) { - btnWrapper.style.left = left; - } - - if (btnWrapper.style.top !== top) { - btnWrapper.style.top = top; - } - } - - intervalId = setInterval(updatePosition, 200); - updatePosition(); - }, - - async handleOnClickSLButton(inputElem, slButton) { - if (InputTools.isLoading) { - return; - } - InputTools.isLoading = true; - slButton.classList.add("loading"); - - let res = await sendMessageToBackground("NEW_RANDOM_ALIAS", { - currentUrl: window.location.href, - }); - if (res.error) { - alert("SimpleLogin Error: " + res.error); - res = { alias: "" }; - } - - InputTools.isLoading = false; - slButton.classList.remove("loading"); - - inputElem.value = res.alias; - }, - - sumPixel(dimensions) { - let sum = 0; - for (const dim of dimensions) { - sum += !isNaN(dim) ? dim : InputTools.dimensionToInt(dim); - } - return sum; - }, - - dimensionToInt(dim) { - try { - const pixel = parseFloat(dim.replace(/[^0-9.]+/g, "")); - return isNaN(pixel) ? 0 : pixel; - } catch (e) { - return 0; - } - }, - }; - - const MutationObserver = - window.MutationObserver || - window.WebKitMutationObserver || - window.MozMutationObserver; - - /** - * Add DOM mutations listener - */ - function addMutationObserver() { - const mutationObserver = new MutationObserver(function (mutations) { - mutations.forEach(function (mutation) { - for (const addedNode of mutation.addedNodes) { - // add SLButton for newly added nodes - InputTools.init(addedNode); - } - - for (const removedNode of mutation.removedNodes) { - // destroy SLButton for removed nodes - InputTools.destroy(removedNode); - } - }); - }); - - const target = document.body; - if (!target) return; - - mutationObserver.observe(target, { - childList: true, - subtree: true, - }); - } +if (!window._hasExecutedSlExtension) { + window._hasExecutedSlExtension = true; /** * Send message to background.js and resolve with the response * @param {string} tag * @param {object} data */ - function sendMessageToBackground(tag, data = null) { + const sendMessageToBackground = (tag, data = null) => { const _browser = window.chrome || browser; return new Promise((resolve) => { try { @@ -226,15 +24,240 @@ if (!window.hasSLButton) { console.error(e); } }); - } + }; - async function initModule() { - SLSettings = await sendMessageToBackground("GET_APP_SETTINGS"); - if (SLSettings.showSLButton) { - InputTools.init(document); - addMutationObserver(); + const slButtonLogic = async () => { + if (!window.hasSLButton) { + window.hasSLButton = true; + + const InputTools = { + isLoading: false, + + // store tracked input elements + trackedElements: [], + + init(target) { + InputTools.queryEmailInputAndApply(target, (element) => { + if (!InputTools.isValidEmailInput(element)) { + return; + } + + // ignore if this elements has already been tracked + const i = InputTools.trackedElements.indexOf(element); + if (i === -1) { + InputTools.trackedElements.push(element); + InputTools.addSLButtonToInput(element); + } + }); + }, + + destroy(target) { + InputTools.queryEmailInputAndApply(target, (element) => { + // remove element from tracking list + const i = InputTools.trackedElements.indexOf(element); + if (i !== -1) { + InputTools.trackedElements.splice(i, 1); + } + }); + }, + + queryEmailInputAndApply(target, actionFunction) { + if (!target.querySelectorAll) return; + const elements = target.querySelectorAll( + "input[type='email'],input[name*='email'],input[id*='email']" + ); + for (const element of elements) { + actionFunction(element); + } + }, + + isValidEmailInput(element) { + const style = getComputedStyle(element); + return ( + // check if element is visible + style.visibility !== "hidden" && + style.display !== "none" && + style.opacity !== "0" && + style.pointerEvents === "auto" && + // check if element is not disabled + !element.disabled && + // for example, we must filter out a checkbox with name*=email + // check if element is text or email input + (element.type === "text" || element.type === "email") + ); + }, + + addSLButtonToInput(inputElem) { + // create wrapper for SL button + const btnWrapper = InputTools.newDiv("sl-button-wrapper"); + const inputSumHeight = inputElem.getBoundingClientRect().height + "px"; + btnWrapper.style.height = inputSumHeight; + btnWrapper.style.width = inputSumHeight; + document.body.appendChild(btnWrapper); + + // create the SL button + const slButton = InputTools.newDiv("sl-button"); + slButton.addEventListener("click", function () { + InputTools.handleOnClickSLButton(inputElem, slButton); + }); + slButton.style.height = inputSumHeight; + slButton.style.width = inputSumHeight; + btnWrapper.appendChild(slButton); + + InputTools.placeBtnToTheRightOfElement(inputElem, btnWrapper); + }, + + newDiv(...className) { + const div = document.createElement("div"); + div.classList.add(...className); + return div; + }, + + placeBtnToTheRightOfElement(inputElem, btnWrapper) { + let intervalId = 0; + + function updatePosition() { + // check is element is removed from trackedElements + const i = InputTools.trackedElements.indexOf(inputElem); + if (i === -1) { + btnWrapper.parentNode.removeChild(btnWrapper); + clearInterval(intervalId); + } + + // get dimension & position of input + const inputCoords = inputElem.getBoundingClientRect(); + const inputStyle = getComputedStyle(inputElem); + const elemWidth = InputTools.dimensionToInt(btnWrapper.style.width); + const pageXOffset = window.pageXOffset; + const pageYOffset = window.pageYOffset; + const buttonXOffset = + SLSettings.SLButtonPosition === "right-inside" + ? -elemWidth * 1.02 + : elemWidth * 0.02; + + // calculate elem position + const left = + InputTools.sumPixel([ + inputCoords.left, + pageXOffset, + inputElem.offsetWidth, + buttonXOffset, + -inputStyle.paddingRight, + ]) + "px"; + + const top = InputTools.sumPixel([inputCoords.top, pageYOffset]) + "px"; + + if (btnWrapper.style.left !== left) { + btnWrapper.style.left = left; + } + + if (btnWrapper.style.top !== top) { + btnWrapper.style.top = top; + } + } + + intervalId = setInterval(updatePosition, 200); + updatePosition(); + }, + + async handleOnClickSLButton(inputElem, slButton) { + if (InputTools.isLoading) { + return; + } + InputTools.isLoading = true; + slButton.classList.add("loading"); + + let res = await sendMessageToBackground("NEW_RANDOM_ALIAS", { + currentUrl: window.location.href, + }); + if (res.error) { + alert("SimpleLogin Error: " + res.error); + res = { alias: "" }; + } + + InputTools.isLoading = false; + slButton.classList.remove("loading"); + + inputElem.value = res.alias; + }, + + sumPixel(dimensions) { + let sum = 0; + for (const dim of dimensions) { + sum += !isNaN(dim) ? dim : InputTools.dimensionToInt(dim); + } + return sum; + }, + + dimensionToInt(dim) { + try { + const pixel = parseFloat(dim.replace(/[^0-9.]+/g, "")); + return isNaN(pixel) ? 0 : pixel; + } catch (e) { + return 0; + } + }, + }; + + const MutationObserver = + window.MutationObserver || + window.WebKitMutationObserver || + window.MozMutationObserver; + + /** + * Add DOM mutations listener + */ + const addMutationObserver = () => { + const mutationObserver = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + for (const addedNode of mutation.addedNodes) { + // add SLButton for newly added nodes + InputTools.init(addedNode); + } + + for (const removedNode of mutation.removedNodes) { + // destroy SLButton for removed nodes + InputTools.destroy(removedNode); + } + }); + }); + + const target = document.body; + if (!target) return; + + mutationObserver.observe(target, { + childList: true, + subtree: true, + }); + }; + + const SLSettings = await sendMessageToBackground("GET_APP_SETTINGS"); + if (SLSettings.showSLButton) { + InputTools.init(document); + addMutationObserver(); + } } - } + }; - initModule(); + const slRegisterListener = () => { + if (!window.hasSlListenerRegistered) { + window.hasSlListenerRegistered = true; + + /** + * Callback called for every event + * @param {MessageEvent} event + */ + const onEvent = async (event) => { + if (event.source !== window) return; + if (event.data.tag && (event.data.tag === "PERFORM_EXTENSION_SETUP")) { + await sendMessageToBackground("EXTENSION_SETUP"); + } + }; + + window.addEventListener("message", onEvent); + } + }; + + slButtonLogic(); + slRegisterListener(); } diff --git a/src/onboarding/Onboarding.vue b/src/onboarding/Onboarding.vue deleted file mode 100644 index 47bd754..0000000 --- a/src/onboarding/Onboarding.vue +++ /dev/null @@ -1,151 +0,0 @@ - - - diff --git a/src/onboarding/index.html b/src/onboarding/index.html deleted file mode 100644 index 2e77c6a..0000000 --- a/src/onboarding/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - SimpleLogin Extension - - - - -
- - - diff --git a/src/onboarding/index.js b/src/onboarding/index.js deleted file mode 100644 index c6c8062..0000000 --- a/src/onboarding/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import Vue from "vue"; -import BootstrapVue from "bootstrap-vue"; -import Onboarding from "./Onboarding"; -import "./styles.scss"; - -Vue.use(BootstrapVue); - -/* eslint-disable no-new */ -new Vue({ - el: "#app", - render: (h) => h(Onboarding), -}); diff --git a/src/onboarding/styles.scss b/src/onboarding/styles.scss deleted file mode 100644 index ed9661c..0000000 --- a/src/onboarding/styles.scss +++ /dev/null @@ -1,67 +0,0 @@ -@import '../popup/App-color.scss'; -@import '../popup/App-scrollbar.scss'; -@import '~bootstrap/scss/bootstrap.scss'; -@import '~bootstrap-vue/src/index.scss'; - -.app { - & > .row { - width: 100vw; - } - - .screen-left { - background: rgb(238,48,124); - background: linear-gradient(144deg, rgba(238,48,124,1) 0%, rgba(170,41,144,1) 100%); - height: 100vh; - } - - .right-content { - position: absolute; - top: 50%; - left: 2em; - transform: translate(0, -50%); - max-height: 100vh; - overflow-y: auto; - - h5 { - font-weight: bold; - } - - .header { - .sl-logo { - width: 200px; - } - margin-bottom: 3em; - } - } - - .left-content { - position: absolute; - top: 50%; - right: 2em; - transform: translate(0, -50%); - text-align: right; - - p { - font-size: 1.2em; - color: white; - opacity: 0.5; - cursor: pointer; - } - - p.active { - opacity: 1; - font-weight: bold; - } - } - - .arrow-up { - position: fixed; - right: 2em; - top: 2em; - opacity: 0.7; - - img { - width: 150px; - } - } -} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 0e593a4..c1df41e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,7 +17,6 @@ const config = { entry: { 'background': './background/index.js', 'popup/popup': './popup/popup.js', - 'onboarding/index': './onboarding/index.js', }, output: { path: __dirname + '/dist', @@ -88,7 +87,6 @@ const config = { { from: 'images', to: 'images' }, { from: 'content_script', to: 'content_script' }, { from: 'popup/popup.html', to: 'popup/popup.html', transform: transformHtml }, - { from: 'onboarding/index.html', to: 'onboarding/index.html' }, { from: 'manifest.json', to: 'manifest.json',