From db2fa44a9320eb916c6f435f099197cbc96415b4 Mon Sep 17 00:00:00 2001 From: Mike Singleton Date: Tue, 26 Apr 2022 21:27:48 -0400 Subject: [PATCH 01/12] First steps towards manifest v3 migration Known issues: WebRTC is currently not available in background scripts of browser extensions (due to them being service workers) --- README.txt | 14 + .../scripts/backgroundPortManager.js | 2 +- app/manifest.json | 36 +- app/scripts.babel/background.js | 256 ++++++++- app/scripts.babel/chat.js | 2 +- app/scripts.babel/inject.js | 2 +- app/scripts.babel/logger.js | 2 +- app/scripts.babel/popup.js | 2 +- gulpfile.js | 4 +- package-lock.json | 519 +++++++++++++++++- package.json | 2 + patches/signalhub+4.9.0.patch | 29 + promotion/IF Window.PNG | Bin 0 -> 10384 bytes promotion/hi 1280x800.png | Bin 0 -> 4198 bytes promotion/hi 440x280.png | Bin 0 -> 2633 bytes promotion/hi 920x680.png | Bin 0 -> 3726 bytes promotion/hi grab.PNG | Bin 0 -> 1727 bytes promotion/hi.psd | Bin 0 -> 237276 bytes 18 files changed, 829 insertions(+), 41 deletions(-) create mode 100644 README.txt create mode 100644 patches/signalhub+4.9.0.patch create mode 100644 promotion/IF Window.PNG create mode 100644 promotion/hi 1280x800.png create mode 100644 promotion/hi 440x280.png create mode 100644 promotion/hi 920x680.png create mode 100644 promotion/hi grab.PNG create mode 100644 promotion/hi.psd diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..aeb3c37 --- /dev/null +++ b/README.txt @@ -0,0 +1,14 @@ +Requires npm and gulp to build + +Versions used to build v1.0.0 of Internet Friends: + +npm v8.4.1 +gulp CLI v2.3.0 +gulp Local v4.0.2 + +To build: + +1) cd to the root directory +2) run 'npm install' +3) run 'gulp' +4) run 'gulp package' \ No newline at end of file diff --git a/app/extension/scripts/backgroundPortManager.js b/app/extension/scripts/backgroundPortManager.js index 9fd73a9..0caa1cf 100644 --- a/app/extension/scripts/backgroundPortManager.js +++ b/app/extension/scripts/backgroundPortManager.js @@ -152,7 +152,7 @@ var backgroundPortManager = function (messageCallback, roomDisconnectCallback){ // Loop through all tabs that are associated with the given room code for (var tabId in _rooms[roomCode]) { - chrome.browserAction.setBadgeText( + chrome.action.setBadgeText( { text: peers > 0 ? peers.toString() : '', tabId: parseInt(tabId) diff --git a/app/manifest.json b/app/manifest.json index 4bebfd0..e2dec59 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_appName__", "version": "1.0.0", - "manifest_version": 2, + "manifest_version": 3, "description": "__MSG_appDescription__", "icons": { "16": "images/icon-16.png", @@ -10,11 +10,12 @@ "default_locale": "en", "incognito": "split", "background": { - "page": "html/background.html" + "service_worker": "scripts/bundle.js" }, "permissions": [ "storage", - "activeTab" + "activeTab", + "tabs" ], "content_scripts": [{ "matches": [ @@ -42,19 +43,24 @@ "run_at": "document_start" }], "web_accessible_resources": [ - "scripts/logger.js", - "scripts/settings.js", - "scripts/backgroundPortManager.js", - "scripts/bundle.js", - "scripts/popup.js", - "html/iframe/chat.html", - "scripts/chat.js", - "styles/core.css", - "styles/chat.css", - "styles/slider.css", - "styles/popup.css" + { + "resources": [ + "scripts/logger.js", + "scripts/settings.js", + "scripts/backgroundPortManager.js", + "scripts/bundle.js", + "scripts/popup.js", + "html/iframe/chat.html", + "scripts/chat.js", + "styles/core.css", + "styles/chat.css", + "styles/slider.css", + "styles/popup.css" + ], + "matches": ["*://*/*"] + } ], - "browser_action": { + "action": { "default_popup": "html/popup.html", "default_icon": { "19": "images/icon-19.png", diff --git a/app/scripts.babel/background.js b/app/scripts.babel/background.js index d3c6389..ae27d3d 100644 --- a/app/scripts.babel/background.js +++ b/app/scripts.babel/background.js @@ -1,5 +1,251 @@ -'use strict'; +var debug = true; +var Logger = { + log: debug ? console.log.bind(console) : function(){} +}; +var IFSettings; +var IFEvents = new EventTarget(); + +// wrap in a self-invoking function to use define private variables & functions +(function () { + // define 'init' event + var initEvent = new Event('settings.init'); + + // define default combo + var _defaultCombo = { + ctrlKey: true, + shiftKey: true, + altKey: false, + key: "Enter" + }; + + // define function for getting a random color + function getRandomIroColor () { + var iroColor = new iro.Color('{a: 1, h: 0, s: 70, v: 90}'); + iroColor.hue += Math.random() * 360; + return iroColor.hslaString; + } + + // if chrome is available + if (chrome && chrome.storage) { + // retrieve settings + chrome.storage.sync.get(null, function(result) { + var storedSettings = result; + + // set IFSettings based on storedSettings, get default values if necessary + IFSettings = { + combo: storedSettings?.combo || _defaultCombo, + disabledSites: storedSettings?.disabledSites || {}, + enableChat: storedSettings?.enableChat === true || storedSettings?.enableChat === undefined, + userColor: storedSettings?.userColor || getRandomIroColor() + } + + // set new settings + chrome.storage.sync.set(IFSettings); + + Logger.log('Settings initialized:') + Logger.log(IFSettings); + IFEvents.dispatchEvent(initEvent); + }); + + // listen for changes to settings + chrome.storage.onChanged.addListener(function (changes, namespace) { + for (let [key, { newValue }] of Object.entries(changes)) { + Logger.log(`Storage key "${key}" in namespace "${namespace}" changed.`); + + // update settings + IFSettings[key] = newValue; + IFEvents.dispatchEvent(new Event('settings.change.' + key)); + + Logger.log(IFSettings); + } + }); + } + // otherwise this is loaded outside an extension + else + { + // set IFSettings with default values + IFSettings = { + combo: _defaultCombo, + disabledSites: {}, + enableChat: true, + userColor: getRandomIroColor() + } + + IFEvents.dispatchEvent(initEvent); + } +}()); + +var backgroundPortManager = function (messageCallback, roomDisconnectCallback){ + // variables ---------------------------------------------------------------- + var _this = {}, + _openTabs = {}, + _rooms = {}, + _messageCallback = null, + _roomDisconnectCallback = null; + + // initialize --------------------------------------------------------------- + _this.init = function (messageCallback, roomDisconnectCallback){ + _messageCallback = messageCallback; + _roomDisconnectCallback = roomDisconnectCallback; + + // send messages from "background.js" + chrome.runtime.onConnect.addListener(processTabPortConnected); + + Logger.log('Internet Friends Background Port Manager Initialized'); + }; + + // private functions -------------------------------------------------------- + function getUrl(port) { + let url; + + if (port.sender.tab.url) { // Chrome + url = new URL(port.sender.tab.url); + } + else // FireFox + { + var tabId = port.sender.tab.id; + var source = port.name; + if (source == 'InternetFriends-main') { + url = new URL(port.sender.tab.url); + } else if (_openTabs[tabId]['InternetFriends-main']) { + url = new URL(_openTabs[tabId]['InternetFriends-main'].sender.url); + } + } + + return url; + } + + function getRoomCodeFromPort(port, url) { + // Room code is based on url and title + // e.g. 'www.google.com/search : test - Google Search' + // The goal is to group users based on the page they're currently on + // Ignoring url.search because many websites url.search values are unique to each user + var url = getUrl(port); + return url.host + url.pathname + " : " + port.sender.tab.title; + } + + // events ------------------------------------------------------------------- + function processTabPortConnected (port){ + // Only process if site not disabled + var websiteUrl = getUrl(port); + + // If websiteUrl is present in disabledSites, InternetFriends is disabled for this site, return + if (IFSettings.disabledSites[websiteUrl.host]) + return; + + var tabId = port.sender.tab.id; + var source = port.name; + var roomCode = getRoomCodeFromPort(port); + + Logger.log('Tab Connected RoomCode: ' + roomCode + ' ID: ' + tabId); + + // Add port to tab + if(!_openTabs[tabId]) + _openTabs[tabId] = {}; + + _openTabs[tabId][source] = port; + + // Create room + if (!_rooms[roomCode]) + _rooms[roomCode] = {}; + + // Add tab to room + _rooms[roomCode][tabId] = true; + + // After both sources have connected + if(_openTabs[tabId]['InternetFriends-chat'] && + _openTabs[tabId]['InternetFriends-main']) + { + // Setup Listeners + _openTabs[tabId]['InternetFriends-chat'].onMessage.addListener(function (event) { processMessage(event, tabId, 'InternetFriends-chat', roomCode) }); + _openTabs[tabId]['InternetFriends-main'].onMessage.addListener(function (event) { processMessage(event, tabId, 'InternetFriends-main', roomCode) }); + _openTabs[tabId]['InternetFriends-chat'].onDisconnect.addListener(function () { processTabPortDisconnect(tabId, 'InternetFriends-chat', roomCode) }); + _openTabs[tabId]['InternetFriends-main'].onDisconnect.addListener(function () { processTabPortDisconnect(tabId, 'InternetFriends-main', roomCode) }); + + // Post loaded messages + _openTabs[tabId]['InternetFriends-chat'].postMessage({event: 'loaded'}); + _openTabs[tabId]['InternetFriends-main'].postMessage({event: 'loaded'}); + } + }; + + function processMessage (message, tabId, source, roomCode) { + _messageCallback(message, roomCode); + + // Forward to iframe + if(source != 'InternetFriends-chat') { + message.data.userId = "localuser"; + _this.tellByTabId(tabId, message); + } + } + + function processTabPortDisconnect (tabId, source, roomCode){ + // process the disconnect + if (_openTabs[tabId]) { + delete _openTabs[tabId][source]; + } + + if(source == 'InternetFriends-main') { + delete _openTabs[tabId]; + delete _rooms[roomCode][tabId]; + + let tabIds = Object.keys(_rooms[roomCode]); + + // If there are no other tabs associated with this room code, disconnect + if (tabIds.length === 0) + _roomDisconnectCallback(roomCode); + } + }; + + // public functions --------------------------------------------------------- + _this.tellByRoomCode = function (roomCode, data){ + if (!_rooms[roomCode]) + return false; + + let success = false; + + // Loop through all tabs that are associated with the given room code + for (var tabId in _rooms[roomCode]) { + success ||= _this.tellByTabId(tabId, data); + } + + // If any tabs were found, this should return true + return success; + }; + + _this.tellByTabId = function (tabId, data){ + if(_openTabs[tabId] && _openTabs[tabId]["InternetFriends-chat"]) { + _openTabs[tabId]["InternetFriends-chat"].postMessage(data); + return true; + } + + return false; + }; + + _this.updateBadgeTextByRoomCode = function (roomCode, peers) { + if (!_rooms[roomCode]) + return false; + + // Loop through all tabs that are associated with the given room code + for (var tabId in _rooms[roomCode]) { + chrome.action.setBadgeText( + { + text: peers > 0 ? peers.toString() : '', + tabId: parseInt(tabId) + } + ); + } + + return true; + }; + + // messages ----------------------------------------------------------------- + _this.init(messageCallback, roomDisconnectCallback); + + return _this; +}; + +import { v4 as uuidv4 } from 'uuid'; var swarm = require('webrtc-swarm') var signalhub = require('signalhub') @@ -15,12 +261,12 @@ var Background = (function() { // receive post messages from 'inject.js' and any iframes _portManager = new backgroundPortManager(processMessageFromBrowser, processRoomDisconnect); - if (chrome && chrome.browserAction) { - chrome.browserAction.setBadgeBackgroundColor({ color: IFSettings.userColor }); + if (chrome && chrome.action) { + chrome.action.setBadgeBackgroundColor({ color: IFSettings.userColor }); // add listener for storage changes IFEvents.addEventListener('settings.change.userColor', function () { - chrome.browserAction.setBadgeBackgroundColor({color: IFSettings.userColor }); + chrome.action.setBadgeBackgroundColor({color: IFSettings.userColor }); }); } @@ -47,7 +293,7 @@ var Background = (function() { _portManager.tellByRoomCode(roomCode, userInfoMessage); var hub = signalhub(roomCode, _local ? ['localhost:8080'] : ['https://if-signalhub.herokuapp.com/']) - _swarms[roomCode] = swarm(hub) + _swarms[roomCode] = swarm(hub, { wrtc: require('wrtc'), uuid: uuidv4() }) _swarms[roomCode].on('peer', function(peer, id) { Logger.log('connected to a new peer:', id) diff --git a/app/scripts.babel/chat.js b/app/scripts.babel/chat.js index bfcbd2f..109e058 100644 --- a/app/scripts.babel/chat.js +++ b/app/scripts.babel/chat.js @@ -27,7 +27,7 @@ var User = function(id, submitCallback) { _inputElement = $('').appendTo(_userElement); setupInputElement(); } else { - var cursorURL = typeof chrome !== "undefined" && chrome.extension ? chrome.extension.getURL('../../images/aero_arrow.png') : './images/aero_arrow.png'; + var cursorURL = typeof chrome !== "undefined" && chrome.runtime ? chrome.runtime.getURL('../../images/aero_arrow.png') : './images/aero_arrow.png'; _mouseElement = $('
').appendTo(_userElement); _mouseBGElm = $('
').appendTo(_mouseElement); _mouseBGElm.css({ 'background-image': 'url(' + cursorURL + ')' }); diff --git a/app/scripts.babel/inject.js b/app/scripts.babel/inject.js index cb0196c..a69f5c3 100644 --- a/app/scripts.babel/inject.js +++ b/app/scripts.babel/inject.js @@ -60,7 +60,7 @@ var Inject = (function() { if (_views[id]) return _views[id]; // iframe initial details - var src = typeof chrome !== "undefined" && chrome.extension ? chrome.extension.getURL('html/iframe/' + id + '.html?view=' + id + '&_' + new Date().getTime()) : './html/iframe/chat.html', + var src = typeof chrome !== "undefined" && chrome.runtime ? chrome.runtime.getURL('html/iframe/' + id + '.html?view=' + id + '&_' + new Date().getTime()) : './html/iframe/chat.html', iframe = $('